MySql插入select查询速度太慢,无法复制1亿行
我有一个由1亿多行组成的表,希望将数据复制到另一个表中。我有1个要求, 1.查询执行不得阻止对这些数据库表的其他操作, 我编写了一个存储过程,如下所示 我计算源表中的行数,然后创建一个循环,但在每次迭代中复制10000行,启动事务并提交它。然后按偏移量读取下一个10000MySql插入select查询速度太慢,无法复制1亿行,mysql,insert,nonblocking,Mysql,Insert,Nonblocking,我有一个由1亿多行组成的表,希望将数据复制到另一个表中。我有1个要求, 1.查询执行不得阻止对这些数据库表的其他操作, 我编写了一个存储过程,如下所示 我计算源表中的行数,然后创建一个循环,但在每次迭代中复制10000行,启动事务并提交它。然后按偏移量读取下一个10000 CREATE PROCEDURE insert_data() BEGIN DECLARE i INT DEFAULT 0; DECLARE iterations INT DEFAULT 0; DECLARE row
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 ;
查询执行时没有锁定表,但在复制了数百万行之后,它变得太慢了。
请提出更好的方法来完成这项任务。
谢谢你 块是关键。希望您使用的是InnoDB,而不是MyIsam,它在记录级使用哪个块,在表级使用哪个块。由于不知道数据或底层硬件的复杂性,每个循环10K记录可能太大 任何插入。。。选择查询对从SELECT中的源表中读取的行执行。但是通过处理较小的行块,锁不会持续太长时间 具有限制的查询。。。当您在源表中前进时,偏移量将越来越慢。在每个块10000行的情况下,您需要运行10000次该查询,每个查询都必须重新开始并扫描整个表以达到新的偏移量 无论您做什么,复制1亿行都需要一段时间。它做了很多工作 我会使用,一个免费的工具,专门为此设计。它以块或子集的形式处理行。它将动态调整块的大小,使每个块花费0.5秒 您的方法和pt archiver之间最大的区别是pt archiver不使用限制。。。偏移量,它沿着主键索引移动,按值而不是按位置选择行块。因此,可以更有效地读取每个块 请回复您的评论: 我预计,减小批处理大小——并增加迭代次数——将使性能问题变得更糟,而不是更好 原因是,在使用带偏移量的LIMIT时,每个查询都必须从表的开头重新开始,并按偏移量值计算行数。当您在表中迭代时,这会变得越来越长 使用OFFSET运行20000个昂贵的查询将比运行10000个类似查询花费更长的时间。最昂贵的部分不是读取5000或10000行,也不是将它们插入目标表。昂贵的部分将一次又一次地跳过约50000000行 相反,您应该通过值而不是偏移来迭代表
INSERT IGNORE INTO Table2(id, field2, field3)
SELECT f1, f2, f3
FROM Table1
WHERE id BETWEEN rowOffset AND rowOffset+limitSize;
在循环之前,查询MINid和MAXid,从最小值开始rowOffset,然后循环到最大值
这就是pt archiver的工作方式。谢谢@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$$
对于我的设置和需要-我必须复制3亿到5亿行。 迅速在一个严重低于规格的服务器上 转储到csv; 将结果拆分为多个文件我的案例中的200k行是最优的 导入分割文件
SELECT a.b.c INTO OUTFILE '/path/dumpfile.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n';
split -a 6 -l 200000 dumpfile.csv FileNamePrefix. // . allows numbering as ext
for f in ./*;
do
mysql -uUser -pPassword dbname -e "set autocommit = 0; set unique_checks = 0; set foreign_key_checks = 0; set sql_log_bin=0; LOAD DATA CONCURRENT INFILE '/path/to/split/files/"$f"' IGNORE INTO TABLE InputTableName FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' (a, b, c);commit;";
echo "Done: '"$f"' at $(date)";
done
谢谢你的回复。问题是我不能使用像pt archiver这样的第三方工具。你能对上述问题提出改进意见吗?从您的回复中我了解到的一件事是将限制从10000减少到任何更小的数字,比如5000,或者?是的,我正在使用inndb。我会试着把10k换成更小的数字,5000怎么样?