建立一个10亿行的MySQL表


18

根据注释中的建议,此问题已从Stack Overflow重新发布,对重复表示歉意。

问题

问题1:随着数据库表的大小变大,如何调整MySQL以提高LOAD DATA INFILE调用的速度?

问题2:使用计算机群集加载不同的csv文件,提高性能还是将其杀死?(这是我明天使用负载数据和批量插入的基准测试任务)

目标

我们正在尝试使用特征检测器和聚类参数的不同组合来进行图像搜索,因此,我们需要能够及时构建大型数据库。

机器信息

该计算机具有256 gig的ram,是否还有另外2台具有相同ram数量的计算机(如果可以通过分配数据库来缩短创建时间)?

表架构

表架构看起来像

+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| match_index   | int(10) unsigned | NO   | PRI | NULL    |                |
| cluster_index | int(10) unsigned | NO   | PRI | NULL    |                |
| id            | int(11)          | NO   | PRI | NULL    | auto_increment |
| tfidf         | float            | NO   |     | 0       |                |
+---------------+------------------+------+-----+---------+----------------+

创建于

CREATE TABLE test 
(
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL AUTO_INCREMENT,
  tfidf FLOAT NOT NULL DEFAULT 0,
  UNIQUE KEY (id),
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

迄今为止的基准测试

第一步是比较批量插入与从二进制文件加载到空表中。

It took:  0:09:12.394571  to do  4,000  inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file

考虑到性能上的差异,我已经从二进制csv文件加载数据,首先,我使用下面的调用加载了包含100K,1M,20M,200M行的二进制文件。

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;

2小时后,我取消了200M行二进制文件(〜3GB csv文件)的加载。

因此,我运行了一个脚本来创建表,并从二进制文件中插入不同数量的行,然后删除表,请参见下图。

在此处输入图片说明

从二进制文件插入1M行花了大约7秒钟。接下来,我决定对一次插入100万行进行基准测试,以查看特定数据库大小是否会遇到瓶颈。一旦数据库达到大约5900万行,平均插入时间将降至大约5,000 /秒

在此处输入图片说明

设置全局key_buffer_size = 4294967296可以稍微提高插入较小二进制文件的速度。下图显示了不同行数的速度

在此处输入图片说明

但是,插入1M行并不能提高性能。

行:1,000,000时间:0:04:13.761428插入/秒:3,940

vs空数据库

行:1,000,000时间:0:00:6.339295插入/秒:315,492

更新资料

使用以下顺序而不是仅使用load data命令来执行加载数据

SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
在此处输入图片说明

因此,就正在生成的数据库大小而言,这看起来很有希望,但其他设置似乎并不影响加载数据文件内调用的性能。

然后,我尝试从不同的计算机加载多个文件,但是由于文件的大小太大,导致其他计算机超时,因此load data infile命令锁定了表

ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction

增加二进制文件中的行数

rows:  10,000,000  seconds rows:  0:01:36.545094  inserts/sec:  103578.541236
rows:  20,000,000  seconds rows:  0:03:14.230782  inserts/sec:  102970.29026
rows:  30,000,000  seconds rows:  0:05:07.792266  inserts/sec:  97468.3359978
rows:  40,000,000  seconds rows:  0:06:53.465898  inserts/sec:  96743.1659866
rows:  50,000,000  seconds rows:  0:08:48.721011  inserts/sec:  94567.8324859
rows:  60,000,000  seconds rows:  0:10:32.888930  inserts/sec:  94803.3646283

解决方案:在MySQL外部预先计算ID,而不是使用自动增量

用建立表

CREATE TABLE test (
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL ,
  tfidf FLOAT NOT NULL DEFAULT 0,
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

与SQL

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"

在此处输入图片说明

随着数据库规模的扩大,使用脚本来预先计算索引似乎已消除了性能下降的问题。

更新2-使用内存表

快3倍左右,而无需考虑将内存表移至基于磁盘的表的成本。

rows:  0  seconds rows:  0:00:26.661321  inserts/sec:  375075.18851
rows:  10000000  time:  0:00:32.765095  inserts/sec:  305202.83857
rows:  20000000  time:  0:00:38.937946  inserts/sec:  256818.888187
rows:  30000000  time:  0:00:35.170084  inserts/sec:  284332.559456
rows:  40000000  time:  0:00:33.371274  inserts/sec:  299658.922222
rows:  50000000  time:  0:00:39.396904  inserts/sec:  253827.051994
rows:  60000000  time:  0:00:37.719409  inserts/sec:  265115.500617
rows:  70000000  time:  0:00:32.993904  inserts/sec:  303086.291334
rows:  80000000  time:  0:00:33.818471  inserts/sec:  295696.396209
rows:  90000000  time:  0:00:33.534934  inserts/sec:  298196.501594

通过将数据加载到基于内存的表中,然后将其分复制到基于磁盘的表,需要10分钟59.71秒的开销,以便通过查询复制107,356,741行

insert into test Select * from test2;

这将导致大约15分钟的时间来加载100M行,这与将其直接插入基于磁盘的表中大致相同。


1
我认为将主键更改为just id应该更快。(尽管我认为您没有在寻找这个)
DavidEG 2011年

大卫,您好,感谢您的评论,很遗憾,没有键,我们需要执行的查询还不够快(本文后面概述了主键选择的逻辑)stackoverflow.com/questions/4282526/mysql-group-by-优化

1
这只是为了测试吗?您可能想看看MySQL MEMORY引擎: dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html 如果您打算将其部署为架构,我很好奇您打算如何从故障中恢复,似乎可以通过MapReduce / Hadoop更好地处理。
多项式

嗨多项式,谢谢你的提示,此刻,我们只是在不同尺度上测试不同功能的探测器,一旦生成数据库,它不会有太大变化(在当前规范反正)

Answers:


4

好问题-解释清楚。

如何调整MySQL以提高LOAD DATA INFILE调用的速度?

您已经为密钥缓冲区设置了很高的设置,但是够了吗?我假设这是64位安装(如果没有,那么您需要做的第一件事就是升级)并且不在MSNT上运行。运行一些测试后,看看mysqltuner.pl的输出。

为了最大程度地使用缓存,您可能会发现在对输入数据进行批处理/预排序时会发现好处(最新版本的“ sort”命令具有许多用于对大型数据集进行排序的功能)。同样,如果您在MySQL之外生成ID号,则可能会更有效。

将使用计算机群集加载不同的csv文件

再次假设您要让输出集表现为单个表,那么您将获得的唯一好处就是分发排序和生成ID的工作-您不需要更多的数据库。OTOH使用数据库集群,您会遇到争用问题(除了性能问题之外,您不应将其视为其他问题)。

如果您可以分片数据并独立处理结果数据集,那么可以,您将获得性能上的好处-但这并没有消除调整每个节点的需要。

检查您至少有4 Gb的sort_buffer_size。

除此之外,性能的限制因素全都与磁盘I / O有关。有很多方法可以解决此问题-但您可能应该考虑在SSD上镜像一组带状数据集,以实现最佳性能。


1
  • 考虑您的限制因素。几乎可以肯定,这是单线程CPU处理。
  • 您已经确定它load data...比插入要快,所以请使用它。
  • 您已经确定,很大的文件(按行号)会大大降低速度。您想将它们分解成碎片。
  • 使用非重叠主键,至少要排队N * CPU集,使用不超过一百万行...可能更少(基准)。
  • 在每个文件中使用顺序的主键块。

如果您真的很想弄虚作假,可以创建一个多线程程序,以将单个文件提供给命名管道的集合并管理插入实例。

总而言之,在将工作负载调整到MySQL时,您不必为此而调整MySQL。


-1

我不记得确切的句法,但是如果它是inno db,则可以关闭外键检查。

您也可以在导入后创建索引,这确实可以提高性能。


如果表中已有的行数明显少于要添加的行数,则推迟索引重建只会提高性能。
symcbean 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.