哪个更快:多个单个INSERT还是一个多行INSERT?

时间:2009-11-24 21:51:33

标签: mysql insert benchmarking

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

13 个答案:

答案 0 :(得分:255)

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

  

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

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

从这一点可以看出,发送一个大型语句将为每个插入语句节省7个开销,在进一步阅读文本时也会说:

  

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

答案 1 :(得分:141)

我知道在问到这个问题差不多两年半之后我就回答了这个问题,但我只是想提供一些我现在正在研究的项目的硬数据,这表明每个项目实际上有多个VALUE与顺序单个VALUE块INSERT语句相比,insert MUCH 更快。

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

执行单个VALUE块(例如,一次一行)需要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的值。)

答案 2 :(得分:17)

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

默认情况下,自动提交已启用,您可能希望将其保留;因此,您执行的每个插入都会执行自己的事务。这意味着如果每行执行一次插入,那么您将为每一行提交一个事务。

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

我当然假设您正在使用事务引擎(通常是innodb)并且您没有调整设置以降低持久性。

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

另一方面,结果是,你真的想要使用多行插页。

有一个限制,它会适得其反,但在大多数情况下,它至少有10,000行。因此,如果您将它们分批最多1,000行,那么您可能很安全。

如果你正在使用MyISAM,还有其他一些东西,但我不会厌倦你。和平。

答案 3 :(得分:8)

尽可能多次在电线上发送插入数据。实际的插入速度应该相同,但是你会看到网络开销减少带来的性能提升。

答案 4 :(得分:7)

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

答案 5 :(得分:4)

您可能想要:

  • 检查自动提交是否已关闭
  • 打开连接
  • 在一次交易中发送多批插入物(大小约为4000-10000行?您看到了)
  • 关闭连接

根据服务器的扩展程度(PostgreSQlOracleMSSQL确定无效),使用多个线程和多个连接执行上述操作。

答案 6 :(得分:3)

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

根据您使用的语言,您可以在编程/脚本语言中创建批处理,然后再转到数据库并将每个插入添加到批处理中。然后,您将能够使用一个连接操作执行大批量。 Here's Java中的一个例子。

答案 7 :(得分:3)

MYSQL 5.5 一个sql insert语句需要大约300到~450ms。 而以下统计数据用于内联多个插入语句。

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

我会说inline是要走的路:)

答案 8 :(得分:2)

我只是做了一个小的基准测试,看起来对于很多线路来说它并不快。这是我插入 280 000 行的结果:

  • 10 000:164.96 秒
  • 到 5 000 时:37 秒
  • 到 1000 时:12.56 秒
  • 600 秒:12.59 秒
  • 到 500 时:13.81 秒
  • 到 250 时:17.96 秒
  • 到 400 时:14.75 秒
  • 按 100:27 秒

看起来 1000 x 1000 是最好的选择。

答案 9 :(得分:2)

这是我做的一个小 PHP bench 的结果:

我正在尝试使用 PHP 8.0、MySQL 8.1 (mysqli) 以 3 种不同方式插入 3000 条记录

多个插入查询,多个事务:

$start = microtime(true);
for($i = 0; $i < 3000; $i++)
{
    mysqli_query($res, "insert into app__debuglog VALUE (null,now(), 'msg : $i','callstack','user','debug_speed','vars')");
}
$end = microtime(true);
echo "Took " . ($end - $start) . " s\n";

做了 5 次,平均:11.132 秒(+/- 0.6 秒)

多个插入查询,单个事务:

$start = microtime(true);
mysqli_begin_transaction($res, MYSQLI_TRANS_START_READ_WRITE);
for($i = 0; $i < 3000; $i++)
{
    mysqli_query($res, "insert into app__debuglog VALUE (null,now(), 'msg : $i','callstack','user','debug_speed','vars')");
}
mysqli_commit($res);
$end = microtime(true);
echo "Took " . ($end - $start) . " ms\n";

5 次测试的结果:0.48s (+/- 0.04s)

单个聚合插入查询

$start = microtime(true);

$values = "";

for($i = 0; $i < 3000; $i++)
{
    $values .= "(null,now(), 'msg : $i','callstack','user','debug_speed','vars')";
    if($i !== 2999)
        $values .= ",";
}
mysqli_query($res, "insert into app__debuglog VALUES $values");

$end = microtime(true);
echo "Took " . ($end - $start) . " ms\n";

5 次测试的结果:0.085s (+/- 0.05s)

所以,对于 3000 行的插入,看起来像:

  • 在单个写入事务中使用多个查询比每次插入使用多个事务进行多个查询快约 22 倍。
  • 使用单个聚合插入语句仍然比使用单个写入事务的多个查询快 6 倍

答案 10 :(得分:1)

在插入时如何优化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。

答案 11 :(得分:0)

多个插入更快,但它已经很好了。另一个thrik是禁用约束检查temprorary使插入更快。你的桌子有没有关系。例如,测试禁用外键并享受速度:

SET FOREIGN_KEY_CHECKS=0;

offcourse你应该在插入后重新打开:

SET FOREIGN_KEY_CHECKS=1;

这是插入大量数据的常用方法。 数据集成可能会中断,因此您必须在禁用外键检查之前对其进行处理。

答案 12 :(得分:0)

我会添加以下信息:根据行的内容,一次过多的行可能会导致 Got a packet bigger than 'max_allowed_packet'

也许可以考虑使用 PHP's array_chunk 之类的函数为大型数据集进行多次插入。