插入select查询的MySql太慢,无法复制1亿行
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插入select查询的MySql太慢,无法复制1亿行相关的知识,希望对你有一定的参考价值。
我有一个包含100多万行的表,并希望将数据复制到另一个表中。我有1个要求,1。查询执行一定不能阻止对这些数据库表的其他操作,我编写了如下存储过程
我计算源表中的行数然后有一个循环但在每次迭代中复制10000行,启动事务并提交它。然后通过偏移读取下一个10000。
CREATE PROCEDURE insert_data()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE iterations INT DEFAULT 0;
DECLARE rowOffset INT DEFAULT 0;
DECLARE limitSize INT DEFAULT 10000;
SET iterations = (SELECT COUNT(*) FROM Table1) / 10000;
WHILE i <= iterations DO
START TRANSACTION;
INSERT IGNORE INTO Table2(id, field2, field3)
SELECT f1, f2, f3
FROM Table1
ORDER BY id ASC
LIMIT limitSize offset rowOffset;
COMMIT;
SET i = i + 1;
SET rowOffset = rowOffset + limitSize;
END WHILE;
END$$
DELIMITER ;
查询在不锁定表的情况下执行,但在复制几百万行后,它变得太慢了。请建议任何更好的方法来完成任务。谢谢!
任何INSERT ... SELECT ...
查询都会对从SELECT中的源表中读取的行执行acquire a SHARED lock。但是通过处理较小的行块,锁定不会持续太长时间。
当您在源表中前进时,LIMIT ... OFFSET
的查询将变得越来越慢。每个块10,000行,您需要运行该查询10,000次,每个必须重新开始并扫描表以到达新的OFFSET。
无论你做什么,复制1亿行都需要一段时间。它做了很多工作。
我会使用pt-archiver,一个为此目的而设计的免费工具。它处理“块”(或子集)中的行。它将动态调整块的大小,使每个块需要0.5秒。
你的方法和pt-archiver之间的最大区别在于pt-archiver不使用LIMIT ... OFFSET
,它沿着主键索引,选择逐行而不是按位置的行。因此,每个块都可以更有效地读取。
你的评论:
我希望减小批量大小 - 并增加迭代次数 - 会使性能问题变得更糟,而不是更好。
原因是当你使用LIMIT
和OFFSET
时,每个查询都必须在表的开头重新开始,并将行计数到OFFSET
值。当您遍历表时,这会变得越来越长。
使用OFFSET
运行20,000个昂贵的查询将比运行10,000个类似查询花费更长的时间。最昂贵的部分不会读取5,000或10,000行,或将它们插入目标表。昂贵的部分将一遍又一遍地跳过~50,000,000行。
相反,您应该通过值而不是偏移来迭代表。
INSERT IGNORE INTO Table2(id, field2, field3)
SELECT f1, f2, f3
FROM Table1
WHERE id BETWEEN rowOffset AND rowOffset+limitSize;
在循环之前,查询MIN(id)和MAX(id),并以最小值启动rowOffset
,并循环到最大值。
这就是pt-archiver的工作方式。
Block是操作词。希望您使用InnoDB(在记录级别阻止)而不是MyIsam(在表级阻塞)。不知道数据的复杂性或下面的硬件,每个循环10K记录可能太大。
谢谢@Bill Karvin我按照你的建议删除了偏移量。以下查询工作得非常好,
DROP PROCEDURE IF EXISTS insert_identifierdataset;
DELIMITER $$
CREATE PROCEDURE insert_data()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE limitSize INT DEFAULT 2000;
DECLARE maxId INT DEFAULT 0;
SET maxId = (SELECT MAX(id) FROM Table1);
WHILE i <= maxId DO
START TRANSACTION;
INSERT IGNORE INTO Table2(id, field1, field2)
SELECT id, field3, field4
FROM Table1
WHERE id> i
ORDER BY id ASC
LIMIT limitSize;
COMMIT;
SET i = i + limitSize;
END WHILE;
END$$
以上是关于插入select查询的MySql太慢,无法复制1亿行的主要内容,如果未能解决你的问题,请参考以下文章