哪个更快:多个单个INSERT或一个多行INSERT?


183

我正在尝试优化将数据插入MySQL的代码的一部分。我应该将INSERT链接起来以制作一个巨大的多行INSERT还是更快地使用多个单独的INSERT?

Answers:


286

https://dev.mysql.com/doc/refman/8.0/zh-CN/insert-optimization.html

插入行所需的时间由以下因素决定,其中数字表示近似比例:

  • 连接:(3)
  • 向服务器发送查询:(2)
  • 解析查询:(2)
  • 插入行:(1×行大小)
  • 插入索引:(1×索引数)
  • 闭幕:(1)

由此可见,发送一条大语句将为您节省每个插入语句7的开销,在进一步阅读本文时还说:

如果要同时从同一客户端插入许多行,请使用具有多个VALUES列表的INSERT语句一次插入几行。这比使用单独的单行INSERT语句要快得多(某些情况下要快很多倍)。


27
如果在同一数据库事务中有多个单个INSERT,此答案如何适用?

2
使用单个insert语句一次可以插入多少行。它允许我一次插入10000行吗?
Naresh Ramoliya '16

10
@Pinch在执行〜1.5k upserts(插入/更新)的同时使用事务将操作花费的时间从〜1.5秒减少到〜0.2秒。换句话说,与单排刀片相比,它的速度提高了86%。该死的。
fgblomqvist,2016年

1
注意:在MSSQL中似乎有很大不同:stackoverflow.com/questions/8635818/…–
marsze

如何使用Prepared Statement插入重复的多个单个插入?
priyabagus

151

我知道我在回答这个问题之前有人问近两年半年后,但我只是想现在该节目确实做得每个刀片有多个值块是提供从项目我工作的一些硬数据MUCH比顺序的单个VALUE块INSERT语句快。

我在C#中为此基准编写的代码使用ODBC将数据从MSSQL数据源(〜19,000行,在开始任何写入之前已读取所有数据)读取到内存中,并将MySql .NET连接器(Mysql.Data。*)填充到通过准备好的语句将数据从内存插入MySQL服务器上的表中。它的编写方式使我可以动态地调整每个准备好的INSERT的VALUE块的数量(即,一次插入n行,在运行之前我可以在其中调整n的值。)我还运行了测试每个n多次。

执行单个VALUE块(例如,一次1行)需要5.7-5.9秒才能运行。其他值如下:

一次2行:3.5-3.5秒一次
5行:2.2-2.2秒一次
10行:1.7-1.7秒一次
50行:1.17-1.18秒一次
100行:1.1-1.4秒
一次500行:1.1-1.2秒一次
1000行:1.17-1.17秒

因此,是的,即使将2或3个写入捆绑在一起,也可以显着提高速度(运行时减少了n倍),直到达到n = 5到n = 10之间的某个位置为止,在这一点上,改善明显下降,而在n = 10到n = 50的范围内,改进几乎可以忽略不计。

希望可以帮助人们决定(a)是否使用多重准备的想法,以及(b)每个语句要创建多少VALUE块(假设您要使用可能足够大以使查询超过最大查询大小的数据的工作)对于MySQL,我相信很多地方默认是16MB,根据服务器上设置的max_allowed_pa​​cket值,它可能更大或更小。)


1
澄清要求:您的时间是“每行秒数”还是“总计秒数”。
EngrStudent 2015年

3
秒总数-因此,每行秒数就是除以〜19,000行的时间。尽管这是一个很小的数字,但是如果您正在寻找一个易于比较的数字,那么也许行/秒是一个更好的指标。
乔恩·克洛斯凯

顺便说一下,有一个为我上面描述关于我的这个相关答案办法一些示例.NET代码:stackoverflow.com/questions/25377357/...
乔恩Kloske

18

一个主要因素将是您是否正在使用事务引擎以及是否自动提交。

默认情况下,自动提交功能是打开的,您可能希望将其保持打开状态。因此,您所做的每个插入都会执行自己的事务。这意味着,如果每行插入一次,则将为每一行提交一个事务。

假设有一个线程,则意味着服务器需要将每行的某些数据同步到磁盘。它需要等待数据到达持久存储位置(希望是RAID控制器中由电池供电的RAM)。这本质上是相当缓慢的,并且在这些情况下可能会成为限制因素。

我当然假设您使用的是事务引擎(通常是innodb),并且您没有进行设置以降低持久性。

我还假设您正在使用单个线程来进行这些插入。使用多个线程会使事情有些混乱,因为某些版本的MySQL在innodb中具有工作组提交功能-这意味着执行自己的提交的多个线程可以共享一次写入事务日志的操作,这很好,因为这意味着与持久性存储的同步次数更少。

另一方面,结果是您真的想使用多行插入。

它会适得其反,但在大多数情况下,至少有10,000行。因此,如果将它们最多批量处理1000行,则可能很安全。

如果您使用的是MyISAM,那么还有很多其他事情,但是我不会让您感到厌倦。和平。


1
某一点后有什么原因会适得其反?我也曾经见过这种情况,但不确定为什么。
Dhruv Gairola

1
您是否知道使用事务在批处理MySQL插入中是否有任何意义。我只是想知道,如果我的底层库(Java JDBC-mysql-connector-java-5.1.30)在提交之前没有真正提交,是否可以省去生成多值SQL命令的麻烦。
RTF 2015年

@RTF我认为您将需要执行一次小型测试来确定您的情况下的行为,因为它是高度实现特定的行为,但是在许多情况下,事务应该提供类似的性能提升。
茉莉·黑格曼

9

一次将尽可能多的插入件通过导线发送出去。实际的插入速度应该相同,但是您会看到减少网络开销所带来的性能提升。


7

通常,对数据库的调用次数越少越好(意味着更快,更有效),因此请尝试以使数据库访问最少的方式对插入代码进行编码。请记住,除非您使用连接池,否则每个数据库访问都必须创建一个连接,执行sql,然后拆除该连接。相当多的开销!


如果使用持久连接怎么办?
dusoft

6
仍然有开销。如果您要进行成千上万次插入,那么单独的往返时间(往返于每个单独的插入)将很快可以感觉到。
RC。

4

你可能想要 :

  • 检查自动提交已关闭
  • 打开连接
  • 在一次交易中发送多批插入内容(大小约为4000-10000行?
  • 紧密连接

根据您的服务器规模(其明确地确定有多么好PostgreSQlOracleMSSQL),做以上多线程和多连接的东西。


3

通常,由于连接开销,多次插入会比较慢。一次执行多个插入将减少每个插入的开销成本。

根据您使用的语言,可以在进入数据库之前用您的编程/脚本语言创建一个批处理并将每个插入内容添加到该批处理中。这样一来,您便可以使用一个connect操作执行大批处理。这是 Java中的示例。


3

MYSQL 5.5一条sql插入语句花费了大约300到〜450毫秒。而以下统计信息用于内联多个插入语句。

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

我会说内联是要走的路:)


0

荒谬的是,在插入时如何优化Mysql和MariaDB。我测试了mysql 5.7和mariadb 10.3,两者之间没有真正的区别。

我已经在具有NVME磁盘,70,000 IOPS,1.1 GB /秒seq吞吐量的服务器上进行了测试,并且这可能是全双工(读和写)。
该服务器也是高性能服务器。
给它20 GB的内存。
数据库完全为空。

进行多行插入时,我收到的速度为每秒5000次插入(使用1MB到10MB的数据块进行尝试)

现在的提示:
如果我添加另一个线程并将其插入到SAME表中,我突然会得到2x5000 / sec。还有一个线程,我总共有15000个/秒

请考虑以下问题:在执行一个线程插入时,这意味着您可以顺序写入磁盘(索引例外)。使用线程时,实际上会降低可能的性能,因为它现在需要进行更多的随机访问。但是现实检查表明,mysql的优化非常差,线程可以提供很多帮助。

此类服务器的实际性能可能约为每秒数百万,CPU处于空闲状态,磁盘处于空闲状态。
原因很清楚,就像MySQL具有内部延迟一样,mariadb也是如此。


@Craftables需要外部开发,它不能在mysql内完成。线程意味着您使用到服务器的多个连接,将查询分为多个块(例如,通过主键将查询分成偶数部分)。使用超大型表使用此方法,我设法获得了10,000倍的性能。如果您使用多个线程并且mysql已高度优化,则可以运行40,000秒的查询可以在2-3分钟内完成。
约翰

@John有趣的,可能有一些真正好的应用程序...但是...如果将查询分为多个块,您将如何处理事务?还要考虑以下情形:表x具有与相同表“ id”相关的“ parent_id”列。在数据中的某处,您有INSERT INTO x(idparent_id)VALUES(1,NULL)。下一组值之一链接到该行。如果拆分成多个块,并且该集合进入另一个块,则可能会在第一个块之前对其进行处理,从而使整个过程失败。知道如何处理吗?
zozo

@zozo这对于批量插入和批量查询很有用。无论如何,事务都会破坏性能,因为它们包含大量的数据缓冲。但是您也可以在多线程插入或查询中使用事务。
约翰

-2

多次插入速度更快,但应该加快。另一个麻烦是禁用临时检查插入约束,快得多。表格是否有没有关系。例如,测试禁用外键并享受速度:

SET FOREIGN_KEY_CHECKS=0;

当然,您应该在插入后通过以下方式将其重新打开:

SET FOREIGN_KEY_CHECKS=1;

这是插入大量数据的常用方法。数据完整性可能会中断,因此在禁用外键检查之前,您应该注意这一点。


1
不知道为什么ppl赞成这一点有两个原因:1.与问题无关。2.这是一个非常糟糕的主意(有一些例外,例如转储或结构性临时变更,但总的来说是不好的)。进行检查是有原因的:为了确保数据一致性而进行检查。之所以放慢速度,是因为它们确保您不会插入或以其他方式更改不应删除的数据。尝试以正确的方式优化查询;在任何关键业务环境中,这都意味着该应用程序将被淘汰,因为无论您多么谨慎,事情都会在某个时候失败。
zozo

1
也许,但是此选项在导入大表时非常有效,并且非常实用,它可以使某些人了解如何更快地插入数据。
MSS
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.