根据注释中的建议,此问题已从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行,这与将其直接插入基于磁盘的表中大致相同。
id
应该更快。(尽管我认为您没有在寻找这个)