MySql insert into select query复制1亿行太慢

标签 mysql insert nonblocking

我有一个包含 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 ... 查询都会执行 acquire a SHARED lock在它从 SELECT 中的源表中读取的行上。但是通过处理较小的行 block ,锁不会持续太久。

使用 LIMIT ... OFFSET 的查询会随着您在源表中的推进而变得越来越慢。在每个 block 10,000 行时,您需要运行该查询 10,000 次,每次都必须重新开始并扫描整个表以达到新的 OFFSET。

无论您做什么,复制 1 亿行都需要一段时间。它做了很多工作。

我会使用 pt-archiver ,一款专为此目的而设计的免费工具。它以“ block ”(或子集)的形式处理行。它将动态调整 block 的大小,以便每个 block 需要 0.5 秒。

您的方法和 pt-archiver 之间最大的区别是 pt-archiver 不使用 LIMIT ... OFFSET,它沿着主键索引遍历,而是按值选择行 block 的位置。所以每个 block 都被更有效地读取。


回复你的评论:

我预计,减小批量大小并增加迭代次数会使性能问题更糟,而不是更好。

原因是当您将 LIMITOFFSET 一起使用时,每个查询都必须从表的开头重新开始,并计算行数直到 >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 的工作方式。

关于MySql insert into select query复制1亿行太慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52099389/

相关文章:

php - 为什么我无法获取文件路径并写入数据库?

mysql - 如何将整个备份文件恢复到 MySql 数据库?

php - 在 mysql 中搜索长字符串中的关键字?

sql - 如何将文本文件的内容插入到 SQL Server 中的表中

node.js - ArangoDB 插入速度极慢

c - 套接字:为什么阻塞 read() 因 ENOTCONN 而失败?

php - 基于空间或其他任何方式对字符串数据进行计数

带有分区postgresql的hibernate插入批处理

networking - 事件驱动编程是如何实现的?

c - C中非阻塞地将数据从一个程序发送到另一个程序