我的目标是在MySQL表中保存大约6千万行以进行高速读取,并且正确地继续插入。
对于产品设计,这些6000万行可以自然地分成3000个块,因此我决定制定一个表格分割策略,将一个60M表分成3000个表。
我为以下测试获取了300万个数据:
1个表中有300万行: 然后平均插入这300万个数据是80秒,每1000个查询(每个查询从这个300万个数据表中获取1000行)的成本大约为10秒。
300万行平均分为3000个表: 将300万个数据插入3000个表:79秒(不是很快); 每1000个查询平均3000个表(其中每个表有1000行):120秒(比上面慢12倍)
为什么?虽然我有3000个表,但它们基本上是由MySQL管理的文件,每个查询只能访问一个只有1000行的表,但为什么它这么慢呢?
我在带有15G RAM的8核机器上运行,具有以下配置:
open_files_limit 300000
table_open_cache 100000
经过2-3次模拟重试后,我还搜索了MySQL“openED files”,如下所示,这对于我的3000表设置似乎没问题?
Opened_tables:9463
如何摆脱这个问题?
-----------编辑和更多的想法-----------
我只是尝试表格分割这一刻的可能性,也许MySQL Merge引擎可以在这方面帮助一点点。
另一方面,也许分区也不错也不是......例如,按照Range的MySQL分区,我可以将Range分配给1000万,然后60M表变成一个有6个分区的表。 ..查询和插入会更快吗?
-----------尝试表分区的更新-----------
同样如下所述,而不是表Sharding,我想到了Table Partition也可能是一个很好的解决方案,特别是当它保持相同的表名并对现有代码影响最小时。
我试图在这个6000万张桌子上制作6个分区;
1)起初,我做了一些看起来像下面的伪代码:
CREATE TABLE `datatable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) NOT NULL DEFAULT 0,
`description` varchar(255),
`datimeutc` datetime,
`datimelocal` datetime,
`value` double,
PRIMARY KEY (`id`),
KEY INDEX_TYPE ON (type)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
PARTITION BY RANGE (id) (
PARTITION p0 VALUES LESS THAN (10000000),
PARTITION p1 VALUES LESS THAN (20000000),
PARTITION p2 VALUES LESS THAN (30000000),
PARTITION p3 VALUES LESS THAN (40000000),
PARTITION p4 VALUES LESS THAN (50000000)
PARTITION p5 VALUES LESS THAN MAXVALUE
);
结果非常好。导入300万个测试数据大约需要1分钟,导入所有6000万个数据总共需要63分钟。
每个查询的搜索时间(从基于60-M分区的表中获取20000行)大约为90毫秒。我对单个6000万个表的查询性能没有任何比较数据,但是90毫秒是合理的值吗?
2)我在字段“type”上尝试了分区,希望在单个分区上限制传入的单个查询,因为MySQL对分区的唯一键有限制,伪代码如下所示:
CREATE TABLE `datatable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) NOT NULL DEFAULT 0,
`description` varchar(255),
`datimeutc` datetime,
`datimelocal` datetime,
`value` double,
KEY (`id`),
KEY INDEX_TYPE ON (type)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
PARTITION BY RANGE (type) (
PARTITION p0 VALUES LESS THAN (500),
PARTITION p1 VALUES LESS THAN (1000),
PARTITION p2 VALUES LESS THAN (1500),
PARTITION p3 VALUES LESS THAN (2000),
PARTITION p4 VALUES LESS THAN (2500)
PARTITION p5 VALUES LESS THAN MAXVALUE
);
此时,当我插入60M数据时,插入时间与第一种情况相比是如此之长。我还没有结果,但到目前为止,仅插入4M数据需要3个小时......
为什么?
我正在考虑,也许我按顺序插入60M,即行Id从1到60000000开始。所以在第一种情况下,我基本上打开并锁定第一个分区插入,一旦插入第一个10M,我打开分区二继续。
另一方面,在分区的情况2),我需要经常随机打开所有6个分区(由'type'而不是'id'设计),因此表锁定和解锁花费了太多时间?这可能是原因吗?
答案 0 :(得分:1)
三千个碎片?那是FAR太多了。 mysqld服务器不得不加扰访问多个分片的数据文件,因此速度正在减慢。
对于单个表,六百万行是一个大数字,但对于您描述的服务器硬件来说,它并不是太多。
在这样的应用程序中,分区的最重要原因是可以更容易地快速删除大量过时的行。如果您的行已过时,则可以按月进行分区。
如果必须对此表进行分片,请尝试使用四个分区。但除非你被性能需求所迫,否则不要对它进行分析。如果我是你,我会让其他应用程序正常工作。然后,一旦一切正常,我将评估所有系统的性能问题(瓶颈)并按严重程度处理它们。
我的预感告诉我,这张大桌子不太可能导致严重的性能问题。
答案 1 :(得分:1)
是MySQL中的拆分表是以下场景的一般好习惯:
在你的原帖中,我认为你主要关注的是第一种情况,所以让我们再讨论一下。
为什么当桌子非常大时,性能会急剧下降?什么是大小边界?这都是关于记忆的。除非您购买了FusionIO或任何类型的SSD系统,否则在I / O命中磁盘时总会出现陡峭的曲线。通常情况下,SATA / SAS磁盘阵列只能执行大约50~200个随机IOPS(写入缓存受BBU保护),与DDR的200,000+随机IOPS相比,速度太慢。当MySQL的变量设置为合理的值并且表大小不大于缓存大小时,性能非常好,但是当表超过该限制时,会发生退化。因此,不要过度优化表结构,除非您知道它们将增长多大,并在整个过程中测试系统限制。由于数据碎片带来的其他副作用,过早分裂表不会显示太多优势,性能甚至可能变差。
基准就像游戏,你知道,它们不能真正代表现实生活中的情况,所以我们需要规范游戏规则。我很好奇你的my.cnf设置,特别是缓冲变量,因为第一个场景的性能在很大程度上取决于内存缓存和磁盘读/写策略。变量是:
我也很好奇SQL查询的编写方式,用于读/写MySQL的线程数。例如,顺序写入一个表只是感觉像顺序写入,速度比随机写入快得多;顺序写入3000个表感觉就像随机写,速度可能不如相反。当创建3000个表时,有3000个.MYI文件和3000个.MYD文件,它们可能在磁盘上不连续(随机I / O会发生),但是1 .MYI和1 .MYD,它们通常可能在磁盘上连续他们自己。这也适用于磁盘读取。但在你的情况下,读取比写入慢得多,我想也许这是因为写入是缓冲的,但如果你是第一次选择行则不读取。当从一个表中读取时,MySQL可以整体预加载key_cache一次,OS也可以预先读取下一个块,因为它们是连续的;但是在多表中,MySQL / OS不能作为一个整体来做。如果您可以尝试生成更多客户端线程来发出查询,则两种情况的性能可能会更接近。
关于你最近关于分区的更新,是的,我认为你可能是正确的,按“类型”进行分区,当你批量插入哪些SQL数据按主键排序但不是'type'时,这听起来像是随机I / O,加上子分区表处理程序切换。