我试图优化我的代码插入数据到MySQL的一部分。我应该链插入,使一个巨大的多行插入或多个单独的插入更快?


当前回答

下面是我做的一个PHP小测试的结果:

我试图在3种不同的方式插入3000条记录,使用PHP 8.0, MySQL 8.1 (mysqli)

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

$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倍

其他回答

下面是我做的一个PHP小测试的结果:

我试图在3种不同的方式插入3000条记录,使用PHP 8.0, MySQL 8.1 (mysqli)

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

$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倍

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

自动提交是默认开启的,你可能想让它保持开启状态;因此,您执行的每个插入操作都会执行自己的事务。这意味着如果您对每行执行一次插入,那么您将为每行提交一个事务。

假设只有一个线程,这意味着服务器需要为每一行同步一些数据到磁盘。它需要等待数据到达一个持久存储位置(最好是RAID控制器中有电池支持的ram)。这本身就相当缓慢,可能会成为这些情况下的限制因素。

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

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

另一方面,结果是,您确实希望使用多行插入。

它会产生反效果,但在大多数情况下至少是10,000行。因此,如果将它们批量处理到1,000行,则可能是安全的。

如果你在使用MyISAM,还有很多其他的东西,但我不会用这些来烦你。和平。

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

根据您使用的语言,您可以在访问db并将每个插入添加到批处理之前,使用编程/脚本语言创建一个批处理。然后,您将能够使用一个连接操作执行大型批处理。这是Java中的一个例子。

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

插入一行所需的时间由以下因素决定,其中数字表示大致的比例: 连接:(3) 向服务器发送查询:(2) 解析查询:(2) 插入行:(1 ×行大小) 插入索引:(1 ×索引个数) 关闭:(1)

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

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

当涉及到插入时,Mysql和MariaDB的优化是多么糟糕,这是荒谬的。 我测试了mysql 5.7和mariadb 10.3,没有真正的区别。

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

在做多行插入时,我收到的速度是每秒5000个插入(尝试了1MB到10MB的数据块)

现在是线索: 如果我添加另一个线程并插入到相同的表中,我突然有2x5000 /秒。 再多一个线程,我就有15000个线程了

考虑一下:当执行一个线程插入时,这意味着您可以按顺序写入磁盘(索引除外)。 当使用线程时,你实际上降低了可能的性能,因为它现在需要做更多的随机访问。 但事实证明,mysql的优化非常糟糕,线程的帮助很大。

这种服务器的实际性能可能是每秒数百万,CPU空闲,磁盘空闲。 原因很明显,mariadb和mysql一样有内部延迟。