当索引不适合key_buffer时,快速MySQL批量加载

时间:2009-11-07 16:39:35

标签: mysql bulkinsert myisam

这里有一个问题,如何正确配置mysql(myisam)以便快速执行批量插入(加载数据infile)。

要导入6个Gb文本文件,15个行,16个列(一些int,一些varchar(255),一个varchar(40),一个char(1)一些日期时间,一个中间文本)。

相对my.conf设置:

key_buffer  = 800M
max_allowed_packet = 160M
thread_cache_size = 80
myisam_sort_buffer_size = 400M
bulk_insert_buffer_size = 400M
delay_key_write = ON
delayed_insert_limit = 10000

有三个索引 - 一个是主要的(autincrement int),一个是唯一的int,另一个是唯一的varchar(40)。

问题在于,在执行load data infile命令后,前3个数据的数据被快速导入(基于table.myd增加的大小 - 5-8 mb / s),但超过3020 Mb限制的uppon导入速度大大降低 - table.myd的大小增加了0.5mb / s。我注意到,导入过程在Key_blocks_unused耗尽时变慢。这些是导入开始时mysql> show status like '%key%';的输出:

mysql> show status like '%key%';
+------------------------+---------+
| Variable_name          | Value   |
+------------------------+---------+
| Com_preload_keys       | 0       | 
| Com_show_keys          | 0       | 
| Handler_read_key       | 0       | 
| Key_blocks_not_flushed | 57664   | 
| Key_blocks_unused      | 669364  | 
| Key_blocks_used        | 57672   | 
| Key_read_requests      | 7865321 | 
| Key_reads              | 57672   | 
| Key_write_requests     | 2170158 | 
| Key_writes             | 4       | 
+------------------------+---------+
10 rows in set (0.00 sec)

这就是3020Mb限制之后的情况,即当key_blocks_unused降到零时,以及批量插入过程变得非常慢的时候:

mysql> show status like '%key%';
+------------------------+-----------+
| Variable_name          | Value     |
+------------------------+-----------+
| Com_preload_keys       | 0         | 
| Com_show_keys          | 0         | 
| Handler_read_key       | 0         | 
| Key_blocks_not_flushed | 727031    | 
| Key_blocks_unused      | 0         | 
| Key_blocks_used        | 727036    | 
| Key_read_requests      | 171275179 | 
| Key_reads              | 1163091   | 
| Key_write_requests     | 41181024  | 
| Key_writes             | 436095    | 
+------------------------+-----------+
10 rows in set (0.00 sec)

根据我的理解,问题非常清楚 - 索引存储在缓存中,但是一旦缓存填满,索引就会逐个写入磁盘,这很慢,因此所有进程都会变慢。如果我基于varchar(40)列禁用唯一索引,因此,所有索引都适合Key_blocks_used(我猜这是直接依赖于key_buffer的变量,不是吗?),所有批量导入都是成功的。所以,我很好奇,如何让mysql立即将所有Key_blocks_used数据放入磁盘,并释放Key_blocks_used ?.我知道它可能正在进行一些即时排序,但我仍然认为应该可以进行一些缓存的RAM磁盘同步,以便成功管理索引,即使它们并非全部适合内存缓存。所以我的问题是“如何配置mysql,以便批量插入可以避免在(几乎)每个索引上写入磁盘,即使所有索引都不适合缓存?”最后也不是最少 - delay_key_write对于给定的表,设置为1,但与禁用时相比,它没有添加任何加速。

提前感谢任何想法,想法,解释和RTM! (:

还有一个小问题 - 在Key_blocks_unused变为0之前,我将如何计算多少varchar(40)索引适合缓存?

P.S。使用$myisamchk --keys-used=0 -rq /path/to/db/tbl_name禁用索引,然后使用$myisamchk -rq /path/to/db/tbl_name重新启用它们,如Mysql docs中所述,这是一种已知的解决方案,只有在批量插入空表时才有效。如果表中已有某些数据,则必须进行索引唯一性检查,因此禁用索引不是解决方案。

2 个答案:

答案 0 :(得分:5)

当您使用“加载数据infile”导入数据时,我认为mysql逐个执行插入,并且每次插入时,它都会尝试更新索引文件.MYI,这可能会减慢导入,因为它消耗了bot每个插入的I / O和CPU资源。

您可以做的是将4个文件添加到导入文件中以禁用表的键并在insert语句的末尾启用它,您应该看到差异。

LOCK TABLES tableName WRITE;
ALTER TABLE tableName DISABLE KEYS;
----
your insert statement from  go here..
----
ALTER TABLE tableName ENABLE KEYS
UNLOCK TABLES;

如果您不想编辑数据文件,请尝试使用mysqldump获取正确的转储文件,并且不应该使用导入数据来解决这个问题。

##Dump the database
mysqldump databaseName > database.sql

##Import the database
mysql databaseName < database.sql

希望这有帮助!

答案 1 :(得分:0)

我不确定您提及的key_buffer是否与key_buffer_size相同。

我遇到过类似的问题。通过将key_buffer_size值提升到1GB之类的问题来解决我的问题。查看我的问题here