简体   繁体   English

MySQL和NoSQL:帮我选择合适的

[英]MySQL and NoSQL: Help me to choose the right one

There is a big database, 1,000,000,000 rows, called threads (these threads actually exist, I'm not making things harder just because of I enjoy it). 有一个大的数据库,1,000,000,000行,称为线程(这些线程实际存在,我不是因为我喜欢它而使事情变得更难)。 Threads has only a few stuff in it, to make things faster: (int id, string hash, int replycount, int dateline (timestamp), int forumid, string title) 线程中只有一些东西,以加快速度:(int id,string hash,int replycount,int dateline(timestamp),int forumid,string title)

Query: 查询:

select * from thread where forumid = 100 and replycount > 1 order by dateline desc limit 10000, 100

Since that there are 1G of records it's quite a slow query. 由于存在1G的记录,因此查询速度非常慢。 So I thought, let's split this 1G of records in as many tables as many forums(category) I have! 所以我想,让我们在尽可能多的论坛(类别)中将这1G记录分开! That is almost perfect. 这几乎是完美的。 Having many tables I have less record to search around and it's really faster. 有很多表我搜索的记录较少,而且速度真快。 The query now becomes: 查询现在变为:

select * from thread_{forum_id} where replycount > 1 order by dateline desc limit 10000, 100

This is really faster with 99% of the forums (category) since that most of those have only a few of topics (100k-1M). 99%的论坛(类别)真的更快,因为大多数论坛只有少数主题(100k-1M)。 However because there are some with about 10M of records, some query are still to slow (0.1/.2 seconds, to much for my app!, I'm already using indexes! ). 但是因为有一些大约有10M的记录,一些查询仍然会变慢(0.1 / .2秒,对我的应用来说太多了! 我已经在使用索引! )。

I don't know how to improve this using MySQL. 我不知道如何使用MySQL改进这一点。 Is there a way? 有办法吗?

For this project I will use 10 Servers (12GB ram, 4x7200rpm hard disk on software raid 10, quad core) 对于这个项目,我将使用10台服务器(12GB内存,4x7200rpm硬盘,软件raid 10,四核)

The idea was to simply split the databases among the servers, but with the problem explained above that is still not enought. 这个想法是简单地在服务器之间拆分数据库,但是上面解释的问题仍然没有得到解决。

If I install cassandra on these 10 servers (by supposing I find the time to make it works as it is supposed to) should I be suppose to have a performance boost? 如果我在这10台服务器上安装cassandra(通过假设我找到时间让它按预期工作)我应该假设性能提升吗?

What should I do? 我该怎么办? Keep working with MySQL with distributed database on multiple machines or build a cassandra cluster? 继续使用MySQL与多台机器上的分布式数据库或构建一个cassandra集群?

I was asked to post what are the indexes, here they are: 我被要求发布索引是什么,这里是:

mysql> show index in thread;
PRIMARY id
forumid
dateline
replycount

Select explain: 选择说明:

mysql> explain SELECT * FROM thread WHERE forumid = 655 AND visible = 1 AND open <> 10 ORDER BY dateline ASC LIMIT 268000, 250;
+----+-------------+--------+------+---------------+---------+---------+-------------+--------+-----------------------------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref         | rows   | Extra                       |
+----+-------------+--------+------+---------------+---------+---------+-------------+--------+-----------------------------+
|  1 | SIMPLE      | thread | ref  | forumid       | forumid | 4       | const,const | 221575 | Using where; Using filesort | 
+----+-------------+--------+------+---------------+---------+---------+-------------+--------+-----------------------------+

You should read the following and learn a little bit about the advantages of a well designed innodb table and how best to use clustered indexes - only available with innodb ! 您应该阅读以下内容并了解一下设计良好的innodb表的优点以及如何最好地使用聚簇索引 - 仅适用于innodb!

http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html

http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/ http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/

then design your system something along the lines of the following simplified example: 然后按照以下简化示例的方式设计您的系统:

Example schema (simplified) 示例模式(简化)

The important features are that the tables use the innodb engine and the primary key for the threads table is no longer a single auto_incrementing key but a composite clustered key based on a combination of forum_id and thread_id. 重要的特性是表使用innodb引擎,而线程表的主键不再是单个auto_incrementing键,而是基于forum_id和thread_id组合的复合聚簇键。 eg 例如

threads - primary key (forum_id, thread_id)

forum_id    thread_id
========    =========
1                   1
1                   2
1                   3
1                 ...
1             2058300  
2                   1
2                   2
2                   3
2                  ...
2              2352141
...

Each forum row includes a counter called next_thread_id (unsigned int) which is maintained by a trigger and increments every time a thread is added to a given forum. 每个论坛行都包含一个名为next_thread_id(unsigned int)的计数器,该计数器由触发器维护,并在每次将线程添加到给定论坛时递增。 This also means we can store 4 billion threads per forum rather than 4 billion threads in total if using a single auto_increment primary key for thread_id. 这也意味着如果为thread_id使用单个auto_increment主键,我们每个论坛可以存储40亿个线程,而不是总共40亿个线程。

forum_id    title   next_thread_id
========    =====   ==============
1          forum 1        2058300
2          forum 2        2352141
3          forum 3        2482805
4          forum 4        3740957
...
64        forum 64       3243097
65        forum 65      15000000 -- ooh a big one
66        forum 66       5038900
67        forum 67       4449764
...
247      forum 247            0 -- still loading data for half the forums !
248      forum 248            0
249      forum 249            0
250      forum 250            0

The disadvantage of using a composite key is that you can no longer just select a thread by a single key value as follows: 使用复合键的缺点是您不能再按单个键值选择线程,如下所示:

select * from threads where thread_id = y;

you have to do: 你必须做:

select * from threads where forum_id = x and thread_id = y;

However, your application code should be aware of which forum a user is browsing so it's not exactly difficult to implement - store the currently viewed forum_id in a session variable or hidden form field etc... 但是,您的应用程序代码应该知道用户正在浏览哪个论坛,因此实现起来并不十分困难 - 将当前查看的forum_id存储在会话变量或隐藏表单字段等中......

Here's the simplified schema: 这是简化的架构:

drop table if exists forums;
create table forums
(
forum_id smallint unsigned not null auto_increment primary key,
title varchar(255) unique not null,
next_thread_id int unsigned not null default 0 -- count of threads in each forum
)engine=innodb;


drop table if exists threads;
create table threads
(
forum_id smallint unsigned not null,
thread_id int unsigned not null default 0,
reply_count int unsigned not null default 0,
hash char(32) not null,
created_date datetime not null,
primary key (forum_id, thread_id, reply_count) -- composite clustered index
)engine=innodb;

delimiter #

create trigger threads_before_ins_trig before insert on threads
for each row
begin
declare v_id int unsigned default 0;

  select next_thread_id + 1 into v_id from forums where forum_id = new.forum_id;
  set new.thread_id = v_id;
  update forums set next_thread_id = v_id where forum_id = new.forum_id;
end#

delimiter ;

You may have noticed I've included reply_count as part of the primary key which is a bit strange as (forum_id, thread_id) composite is unique in itself. 您可能已经注意到我已将reply_count包含在主键中,这有点奇怪,因为(forum_id,thread_id)复合本身是唯一的。 This is just an index optimisation which saves some I/O when queries that use reply_count are executed. 这只是一个索引优化,它可以在执行使用reply_count的查询时节省一些I / O. Please refer to the 2 links above for further info on this. 有关详细信息,请参阅上面的2个链接。

Example queries 示例查询

I'm still loading data into my example tables and so far I have a loaded approx. 我仍在将数据加载到我的示例表中,到目前为止我已经加载了大约。 500 million rows (half as many as your system). 5亿行(是系统的一半)。 When the load process is complete I should expect to have approx: 当加载过程完成后,我应该有大约:

250 forums * 5 million threads = 1250 000 000 (1.2 billion rows)

I've deliberately made some of the forums contain more than 5 million threads for example, forum 65 has 15 million threads: 我故意让一些论坛包含超过500万个线程,例如,论坛65有1500万个线程:

forum_id    title   next_thread_id
========    =====   ==============
65        forum 65      15000000 -- ooh a big one

Query runtimes 查询运行时

select sum(next_thread_id) from forums;

sum(next_thread_id)
===================
539,155,433 (500 million threads so far and still growing...)

under innodb summing the next_thread_ids to give a total thread count is much faster than the usual: 在innodb总结下,next_thread_ids给出的总线程数比通常快得多:

select count(*) from threads;

How many threads does forum 65 have: 论坛65有多少个主题:

select next_thread_id from forums where forum_id = 65

next_thread_id
==============
15,000,000 (15 million)

again this is faster than the usual: 再次这比通常更快:

select count(*) from threads where forum_id = 65

Ok now we know we have about 500 million threads so far and forum 65 has 15 million threads - let's see how the schema performs :) 好了,我们知道到目前为止我们有大约5亿个线程,而论坛65有1500万个线程 - 让我们看看架构如何执行:)

select forum_id, thread_id from threads where forum_id = 65 and reply_count > 64 order by thread_id desc limit 32;

runtime = 0.022 secs

select forum_id, thread_id from threads where forum_id = 65 and reply_count > 1 order by thread_id desc limit 10000, 100;

runtime = 0.027 secs

Looks pretty performant to me - so that's a single table with 500+ million rows (and growing) with a query that covers 15 million rows in 0.02 seconds (while under load !) 看起来非常高效 - 所以这是一个包含500多万行(并且还在增长)的单个表,其查询在0.02秒内覆盖了1500万行(在加载时!)

Further optimisations 进一步优化

These would include: 这些包括:

  • partitioning by range 按范围划分

  • sharding 分片

  • throwing money and hardware at it 扔钱和硬件

etc... 等等...

hope you find this answer helpful :) 希望你觉得这个答案很有帮助:)

EDIT : Your one-column indices are not enough. 编辑 :您的单列索引是不够的。 You would need to, at least, cover the three involved columns. 您至少需要涵盖三个相关列。

More advanced solution: replace replycount > 1 with hasreplies = 1 by creating a new hasreplies field that equals 1 when replycount > 1 . 更高级的解决方案:使用hasreplies = 1替换replycount > 1hasreplies = 1是创建一个新的hasreplies字段,当replycount > 1时,该字段等于replycount > 1 Once this is done, create an index on the three columns, in that order: INDEX(forumid, hasreplies, dateline) . 完成此操作后,按顺序在三列上创建索引: INDEX(forumid, hasreplies, dateline) Make sure it's a BTREE index to support ordering. 确保它是支持订购的BTREE索引。

You're selecting based on: 您选择的依据是:

  • a given forumid 一个给定的forumid
  • a given hasreplies 一个给定的hasreplies
  • ordered by dateline dateline排序

Once you do this, your query execution will involve: 执行此操作后,您的查询执行将涉及:

  • moving down the BTREE to find the subtree that matches forumid = X . 向下移动BTREE以找到与forumid = X匹配的子树。 This is a logarithmic operation (duration : log(number of forums)). 这是一个对数操作(持续时间:log(论坛数))。
  • moving further down the BTREE to find the subtree that matches hasreplies = 1 (while still matching forumid = X ). 进一步向下移动BTREE以找到匹配hasreplies = 1的子树(同时仍然匹配forumid = X )。 This is a constant-time operation, because hasreplies is only 0 or 1. 这是一个恒定时间操作,因为hasreplies只有0或1。
  • moving through the dateline-sorted subtree in order to get the required results, without having to read and re-sort the entire list of items in the forum. 移动日期行排序的子树以获得所需的结果,而无需阅读和重新排序论坛中的整个项目列表。

My earlier suggestion to index on replycount was incorrect, because it would have been a range query and thus prevented the use of a dateline to sort the results (so you would have selected the threads with replies very fast, but the resulting million-line list would have had to be sorted completely before looking for the 100 elements you needed). 我之前关于replycount索引的建议是不正确的,因为它本来是一个范围查询,因此阻止了使用dateline行对结果进行排序(所以你会选择带有回复的线程非常快,但结果是百万行列表在寻找你​​需要的100个元素之前,必须完全排序。

IMPORTANT : while this improves performance in all cases, your huge OFFSET value (10000!) is going to decrease performance, because MySQL does not seem to be able to skip ahead despite reading straight through a BTREE. 重要提示 :虽然这可以在所有情况下提高性能,但是你的巨大OFFSET值(10000!)会降低性能,因为尽管直接通过BTREE阅读,MySQL似乎无法跳过。 So, the larger your OFFSET is, the slower the request will become. 因此,OFFSET越大,请求就越慢。

I'm afraid the OFFSET problem is not automagically solved by spreading the computation over several computations (how do you skip an offset in parallel, anyway?) or moving to NoSQL. 我担心OFFSET问题不能通过将计算扩展到多个计算来自动解决(无论如何,你如何跳过并行偏移?)或转移到NoSQL。 All solutions (including NoSQL ones) will boil down to simulating OFFSET based on dateline (basically saying dateline > Y LIMIT 100 instead of LIMIT Z, 100 where Y is the date of the item at offset Z ). 所有解决方案(包括NoSQL解决方案)将归结为基于日期dateline模拟OFFSET(基本上说dateline > Y LIMIT 100而不是LIMIT Z, 100 ,其中Y是偏移Z处项目的日期)。 This works, and eliminates any performance issues related to the offset, but prevents going directly to page 100 out of 200. 这有效,并消除了与偏移相关的任何性能问题,但阻止直接进入200页中的第100页。

There is are part of question which related to NoSQL or MySQL option. 有一些问题与NoSQL或MySQL选项有关。 Actually this is one fundamental thing hidden here. 实际上这是隐藏在这里的一个基本内容。 SQL language is easy to write for human and bit difficult to read for computer. SQL语言很容易为人类编写,有点难以为计算机阅读。 In high volume databases I would recommend to avoid SQL backend as this requires extra step - command parsing. 在高容量数据库中,我建议避免使用SQL后端,因为这需要额外的步骤 - 命令解析。 I have done extensive benchmarking and there are cases when SQL parser is slowest point. 我已经做了大量的基准测试,有些情况下SQL解析器是最慢的。 There is nothing you can do about it. 你无能为力。 Ok, you can possible use pre-parsed statements and access them. 好的,您可以使用预解析的语句并访问它们。

BTW, it is not wide known but MySQL has grown out from NoSQL database. BTW,它并不为人所知,但是MySQL已经从NoSQL数据库发展而来。 Company where authors of MySQL David and Monty worked was data warehousing company and they often had to write custom solutions for uncommon tasks. MySQL David和Monty的作者所在的公司是数据仓库公司,他们经常不得不为不常见的任务编写自定义解决方案。 This leaded to big stack of homebrew C libraries used to manually write database functions when Oracle and other were performing poorly. 这导致了大量的自制C库,用于在Oracle和其他人表现不佳时手动编写数据库函数。 SQL was added to this nearly 20 years old zoo on 1996 for fun. 为了好玩,SQL于1996年被添加到这个近20年的动物园。 What came after you know. 你知道后发生了什么。

Actually you can avoid SQL overhead with MySQL. 实际上你可以避免使用MySQL的SQL开销。 But usually SQL parsing is not the slowest part but just good to know. 但通常SQL解析不是最慢的部分,但只是很好知道。 To test parser overhead you may just make benchmark for "SELECT 1" for example ;). 要测试解析器开销,您可以为“SELECT 1”制作基准,例如;)。

You should not be trying to fit a database architecture to hardware you're planning to buy, but instead plan to buy hardware to fit your database architecture. 您不应该尝试将数据库体系结构适用于您计划购买的硬件,而是计划购买适合您数据库体系结构的硬件。

Once you have enough RAM to keep the working set of indexes in memory, all your queries that can make use of indexes will be fast. 一旦有足够的RAM来保存内存中的工作索引集,所有可以使用索引的查询都会很快。 Make sure your key buffer is set large enough to hold the indexes. 确保您的密钥缓冲区设置得足够大以容纳索引。

So if 12GB is not enough, don't use 10 servers with 12GB of RAM, use fewer with 32GB or 64GB of RAM. 因此,如果12GB还不够,请不要使用10个服务器和12GB内存,少用32GB或64GB内存。

Indices are a must - but remember to choose the right type of index: BTREE is more suitable when using queries with "<" or ">" in your WHERE clauses, while HASH is more suitable when you have many distinct values in one column and you are using "=" or "<=>" in your WHERE clause. 索引是必须的 - 但要记住选择正确的索引类型:当在WHERE子句中使用带有“<”或“>”的查询时,BTREE更合适,而当一列中有许多不同的值时,HASH更适合您在WHERE子句中使用“=”或“<=>”。

Further reading http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html 进一步阅读http://dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM