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

标签 mysql batch-processing

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

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

开始交易; INSERT INTO my_table(等批量插入); INSERT INTO my_table(等批量插入); INSERT INTO my_table(等批量插入); INSERT INTO my_table(等批量插入); (更多的) 犯罪; 设置自动提交 = 0;

选项B

将 my_table 复制到 my_table_temp INSERT INTO my_table_temp(等批量插入); INSERT INTO my_table_temp(等批量插入); INSERT INTO my_table_temp(等批量插入); INSERT INTO my_table_temp(等批量插入); (更多的) 将 my_table 重命名为 my_table_old; 将 my_table_temp 重命名为 my_table;

我以前用过第二种方法,效果很好。只有很少的时间会出现问题,即重命名表所花费的时间。

但我的困惑是:如果这是最好的解决方案,那么 START TRANSACTION/COMMIT 有什么意义?这肯定是为了解决我所描述的问题而发明的,不是吗?

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

最佳答案

then what's the point of START TRANSACTION/COMMIT? Surely that was invented to take care of the thing I'm describing, no?

是的,没错。在 InnoDB 中,由于其 MVCC architecture ,作者永远不会阻止读者。您不必担心批量插入会阻塞读者。

异常(exception)情况是,如果您使用 SELECT...FOR UPDATESELECT...LOCK IN SHARE MODE 进行锁定读取 .这些可能会与 INSERT 冲突,具体取决于您选择的数据,以及它是否需要插入新数据的位置的间隙锁。

同样,LOAD DATA INFILE 不会阻止表的非锁定读取器。

您可能希望在我的演示文稿 Load Data Fast! 中看到我获得的批量加载数据的结果。

There's only a tiny amount of time where something might be wrong which is the time it takes to rename the tables.

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

RENAME my_table TO my_table_old, my_table_temp TO my_table;

回复你的评论:

what if I have indexes?

让索引在您执行 INSERT 或 LOAD DATA INFILE 时增量更新。 InnoDB 将在其他并发读取使用索引时执行此操作。

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

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

why do I need to wrap the thing in "START TRANSACTION/COMMIT"?

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

事务的次要目的是减少开销。如果您依赖自动提交而不是显式启动和提交,您仍在使用事务——但自动提交会隐式启动并为每个 INSERT 语句提交一个事务。启动和提交的开销很小,但如果执行 100 万次,开销就会加起来。

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

因此,出于原子更新的逻辑原因和善待硬件的物理原因,当您有一些批量工作要做时,请使用事务。

但是,我不想吓唬你使用事务来不恰本地分组。在您执行某些其他类型的 UPDATE 之后,请务必立即提交您的工作。让交易无限期地挂起也不是一个好主意。 MySQL 可以处理普通日常工作的提交率。当您需要快速连续地进行大量更改时,我建议进行批处理。

关于MySQL:执行这些多批处理 INSERT 的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47953202/

相关文章:

php - sql 中的注册符号未正确显示

mysql - 更新 WHERE IN() mysql

matlab - For 循环中的 %%G 使用以下命令从索引更改为 %G

java - 将单个参数而不是列表传递给 ItemWriter 接口(interface)的 Write() 方法

php - 向数据库提交多个复选框值

mysql - 从 left join 获取所有用户 id

php - 如何使用下一个和上一个按钮功能显示每页 5 个帖子?

java - 为什么 Spring Boot Batch 作业只运行一次?

Windows批量给文件名加前缀,为什么要加两次?

redis - 实时分析处理系统设计