MySQL:执行这些多批量INSERT的最佳方法是什么?

时间:2017-12-23 14:14:53

标签: mysql batch-processing

我有一个MySQL数据库(InnoDB,如果这很重要),我想添加很多行。我想在生产数据库上执行此操作,因此不会出现停机。每次(大约每天一次)我想向数据库添加大约1M行,批量为10k(从我运行的一些测试中,这似乎是最小化时间的最佳批量)。当我在做这些插入时,表需要是可读的。这样做的“正确”方法是什么?对于初学者,你可以假设没有索引。

选项A:https://dev.mysql.com/doc/refman/5.7/en/commit.html

START TRANSACTION; INSERT INTO my_table (etc etc batch insert); INSERT INTO my_table (etc etc batch insert); INSERT INTO my_table (etc etc batch insert); INSERT INTO my_table (etc etc batch insert); (more) COMMIT; SET autocommit = 0;

选项B

copy my_table into my_table_temp INSERT INTO my_table_temp (etc etc batch insert); INSERT INTO my_table_temp (etc etc batch insert); INSERT INTO my_table_temp (etc etc batch insert); INSERT INTO my_table_temp (etc etc batch insert); (more) RENAME my_table TO my_table_old; RENAME my_table_temp TO my_table;

之前我使用过第二种方法并且它有效。只有很短的时间可能会出现错误,这是重命名表所需的时间。

但我的困惑是:如果这是最好的解决方案,那么START TRANSACTION / COMMIT的重点是什么?当然,这是为了照顾我所描述的东西而发明的,不是吗?

奖金问题:如果我们有索引怎么办?我的情况很容易适应,只需关闭临时表中的索引,并在插入完成后和重命名之前重新打开它们。选项A怎么样?似乎很难与带索引的插入进行协调。

2 个答案:

答案 0 :(得分:2)

  

那么START TRANSACTION / COMMIT是什么意思?当然,这是为了照顾我所描述的事物而发明的,不是吗?

是的,确切地说。在InnoDB中,由于它的MVCC architecture,作家从不阻止读者。您不必担心批量插入会阻止读者。

例外情况是您使用SELECT...FOR UPDATESELECT...LOCK IN SHARE MODE 锁定读取。这些可能会与INSERT冲突,具体取决于您选择的数据,以及是否需要插入新数据的间隙锁定。

同样LOAD DATA INFILE不会阻止表格的非锁定读者。

您可能希望在我的演示文稿Load Data Fast!

中看到我为批量加载数据而获得的结果
  

只有很短的时间可能会出现错误,这就是重命名表所需的时间。

没有必要为批量INSERT执行表交换,但是对于它的价值,如果您确实需要这样做,您可以在一个语句中执行多个表重命名。该操作是原子操作,因此任何并发事务都不可能潜入其中。

RENAME my_table TO my_table_old, my_table_temp TO my_table;

重新评论:

  

如果我有索引怎么办?

在执行INSERT或LOAD DATA INFILE时,让索引以递增方式更新。 InnoDB将执行此操作,而其他并发读取正在使用索引。

在INSERT期间更新索引会有开销,但通常最好让INSERT花费更长时间而不是禁用索引。

如果禁用索引,则所有并发客户端都无法使用它。其他查询会变慢。此外,当您重新启用索引时,这将锁定表并在重建索引时阻止其他查询。避免这种情况。

  

为什么我需要将事物包裹在" START TRANSACTION / COMMIT"?

事务的主要目的是将应该作为一个更改提交的更改分组,以便其他并发查询看不到部分完成状态的更改。理想情况下,我们会在一次交易中为您的批量加载执行所有INSERT。

交易的第二个目的是减少开销。如果您依赖自动提交而不是显式启动和提交,您仍然使用事务 - 但是autocommit隐式启动并为每个INSERT语句提交一个事务。启动和提交的开销很小,但如果你做了100万次就会增加。

还有减少个别交易数量的实际物理原因。 InnoDB默认在每次提交后执行文件系统同步,以确保数据安全地存储在磁盘上。如果发生崩溃,这对于防止数据丢失非常重要。但文件系统同步不是免费的。您每秒只能执行有限数量的同步(这取决于您使用的磁盘类型)。因此,如果您尝试为单个事务执行100万个同步,但是您的磁盘每秒只能实际执行100个同步(这对于非SSD类型的单个硬盘而言是典型的),那么您的批量加载将至少需要10,000秒这是将批量INSERT分组到批次中的一个很好的理由。

因此,出于原子更新的逻辑原因以及对硬件友好的物理原因,在进行大量工作时使用事务。

但是,我不想吓唬你使用交易来不恰当地分组。在执行其他类型的更新后,请立即提交您的工作。让交易在无限的时间内保持开放也不是一个好主意。 MySQL可以处理普通日常工作的提交率。当你需要快速连续地进行大量的批量更改时,我建议批处理工作。

答案 1 :(得分:-1)

我认为最好的方法是LOAD DATA IN FILE