为啥批量插入/更新更快?批量更新如何工作?

Posted

技术标签:

【中文标题】为啥批量插入/更新更快?批量更新如何工作?【英文标题】:Why are batch inserts/updates faster? How do batch updates work?为什么批量插入/更新更快?批量更新如何工作? 【发布时间】:2010-11-03 15:59:10 【问题描述】:

为什么批量插入更快?是因为插入单行的连接和设置开销对于一组行是相同的吗?还有哪些其他因素使批量插入更快?

批量更新如何工作?假设表没有唯一性约束,插入语句实际上对批处理中的其他插入语句没有任何影响。但是,在批量更新期间,更新可能会改变表的状态,因此可能会影响批量中其他更新查询的结果。

我知道批量插入查询的语法是在一个大查询中拥有所有插入值。批量更新查询是什么样的?例如如果我有表单的单个更新查询:

update <table> set <column>=<expression> where <condition1>
update <table> set <column>=<expression> where <condition2>
update <table> set <column>=<expression> where <condition3>
update <table> set <column>=<expression> where <condition4>

当它们被批量使用时会发生什么。单个查询会是什么样子?

批量插入和更新是 SQL 标准的一部分吗?

【问题讨论】:

【参考方案1】:

其他帖子解释了为什么批量语句更快以及如何使用文字值来实现。

我认为知道如何使用占位符很重要。不使用占位符可能会导致巨大的命令字符串、引用/转义错误,从而导致易于 SQL 注入的应用程序。

在 PostgreSQL >= 9.1 中使用占位符批量插入

将任意数量的行插入到表“mytable”中,由列“col1”、“col2”和“col3”组成,all in one got(一个语句,一个事务):

INSERT INTO mytable (col1, col2, col3)
 VALUES (unnest(?), unnest(?), unnest(?))

您需要为此语句提供三个参数。第一个必须包含第一列的所有值,依此类推。因此,所有参数都必须是等长的列表/向量/数组。

在 PostgreSQL >= 9.1 中使用占位符进行批量更新

假设您的表名为“mytable”。它由“key”和“value”列组成。

update mytable 
  set value = data_table.new_value
  from 
    (select unnest(?) as key, unnest(?) as new_value) as data_table
  where mytable.key = data_table.key

我知道,这并不容易理解。它看起来像混淆的 SQL。另一方面:它可以工作,可以扩展,无需任何字符串连接即可工作,安全且速度极快。

您需要为此语句提供两个参数。第一个必须是包含“键”列的所有值的列表/向量/数组。当然,第二个必须包含列“value”的所有值。

如果您达到大小限制,您可能需要查看 COPY INTO ... FROM STDIN (PostgreSQL)。

【讨论】:

【参考方案2】:

我正在寻找关于“批量/批量”更新的同一主题的答案。人们经常通过将其与具有多个值集(“批量”部分)的插入子句进行比较来描述问题。

INSERT INTO mytable (mykey, mytext, myint)
VALUES 
  (1, 'text1', 11),
  (2, 'text2', 22),
  ...

明确的答案仍然避开我,但我在这里找到了解决方案:http://www.postgresql.org/docs/9.1/static/sql-values.html

说清楚:

UPDATE mytable
SET 
  mytext = myvalues.mytext,
  myint = myvalues.myint
FROM (
  VALUES
    (1, 'textA', 99),
    (2, 'textB', 88),
    ...
) AS myvalues (mykey, mytext, myint)
WHERE mytable.mykey = myvalues.mykey

它具有相同的“大容量”属性,也就是一个语句包含大量数据。

【讨论】:

这是一个了不起的答案。我在这里用过这个:***.com/questions/55052395/…【参考方案3】:

在批量更新中,数据库针对一组数据工作,在逐行更新中,它必须运行与可能多次相同的命令,因为有行。因此,如果您批量插入一百万行,则该命令将被发送和处理一次,而在逐行更新中,该命令将被发送和处理一百万次。这也是您永远不想在 SQL Server 或相关子查询中使用游标的原因。

SQL server 中基于集合的更新示例:

update mytable
set myfield = 'test'
where myfield is null

这将一步更新所有 100 万条为空的记录。游标更新(即以非批处理方式更新一百万行的方式)将遍历每一行并更新它。

批量插入的问题在于批量的大小。如果您尝试一次更新太多记录,数据库可能会在整个过程中锁定表,从而将所有其他用户锁定在外。所以你可能需要做一个循环,一次只占用部分批次(但几乎任何一次大于一行的数字都会比一次快一行)这比更新、插入或删除整个批处理,但比逐行操作更快,并且在用户不尝试查看和更新​​同一个表中的其他记录时,可能需要在具有许多用户且可用停机时间很少的生产环境中。批处理的大小很大程度上取决于数据库结构以及正在发生的情况(带有触发器和大量约束的表和带有大量字段的表一样慢,因此需要较小的批处理)。

【讨论】:

大更新会锁定用户的想法只适用于糟糕的数据库或糟糕的应用程序开发人员。 SQL Server 自 V7.0 以来提供了标准的 4 种事务隔离级别,您必须做一些完全错误的事情才能通过插入数据来阻止任何事情。【参考方案4】:

为什么批量插入更快?

出于多种原因,但主要的三个是:

查询不需要重新解析。 值在一次往返中传输到服务器 命令位于单个事务中

是因为插入单行的连接和设置开销对于一组行是相同的吗?

部分是的,见上文。

批量更新如何工作?

这取决于RDBMS

Oracle 中,您可以将所有值作为集合传输,并将此集合用作JOIN 中的表。

PostgreSQLmysql 中,可以使用以下语法:

INSERT
INTO    mytable
VALUES 
        (value1),
        (value2),
        …

您还可以准备一次查询并在某种循环中调用它。通常在客户端库中有方法可以做到这一点。

假设表没有唯一性约束,插入语句实际上对批处理中的其他插入语句没有任何影响。但是,在批量更新期间,更新可能会改变表的状态,因此会影响批量中其他更新查询的结果。

是的,您可能会或可能不会从这种行为中受益。

我知道批量插入查询的语法是在一个大查询中拥有所有插入值。批量更新查询是什么样的?

Oracle 中,您在连接中使用集合:

MERGE
INTO    mytable
USING   TABLE(:mycol)
ON      …
WHEN MATCHED THEN
UPDATE
SET     …

PostgreSQL:

UPDATE  mytable
SET     s.s_start = 1
FROM    (
        VALUES
        (value1),
        (value2),
        …
        ) q
WHERE   …

【讨论】:

您能解释一下如何使用最后指定的语句吗?我不太了解它,但它可能是我一直在寻找的东西。 @Quassnoi 我认为您可以通过更好地解释“批量准备语句”和多行插入/更新(和/或两者的组合)之间的区别来改进帖子。 我猜 OP 是在谈论 JDBC 批处理(Statement.addBatch()Statement.executeBatch())而不是 DBMS 特定语法 @a_horse_with_no_name:“单个查询会是什么样子” - 这对我来说看起来像是 DBMS 特有的。不错的死灵评论,我记得在湖滩上回答过! 这里解释了一点关于解析的内容。 docs.oracle.com/cd/B28359_01/server.111/b28318/…

以上是关于为啥批量插入/更新更快?批量更新如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

mybatis批量插入和批量更新

HIbernate 批量插入或更新在 Spring Boot 中不起作用

MS SQL 批量更新\插入

jdbc-批量插入批量删除批量更新

使用 Mongoid 批量插入/更新?

Java--MyBatis批量插入批量更新和批量删除