【问题标题】:Postgres, update and lock orderingPostgres,更新和锁定排序
【发布时间】:2014-12-03 03:00:31
【问题描述】:

我正在开发 Postgres 9.2。

有 2 个 UPDATE,每个都在自己的事务中。一个看起来像:

UPDATE foo SET a=1 WHERE b IN (1,2,3,4);

另一个类似:

UPDATE foo SET a=2 WHERE b IN (1,2,3,4);

这些可能同时运行,实际上在“IN”表达式中有 500 多个。 我有时会看到死锁。 'IN' 表达式中的项目顺序是否真的可能不会影响真正的锁定顺序?

【问题讨论】:

    标签: sql postgresql transactions locking deadlock


    【解决方案1】:

    UPDATE 命令中没有ORDER BY
    但是有SELECT。在子查询中使用row-level lockingFOR UPDATE clause

    UPDATE foo f
    SET    a = 1
    FROM (
       SELECT b FROM foo
       WHERE  b IN (1,2,3,4)
       ORDER BY b
       FOR   UPDATE
       ) upd
    WHERE f.b = upd.b;

    当然,b 必须是 UNIQUE,否则您需要在 ORDER BY 子句中添加更多表达式以使其明确。

    并且您需要对 all UPDATEDELETESELECT .. FOR UPDATE 语句执行相同的顺序。

    相关,有更多细节:

    【讨论】:

      【解决方案2】:

      是的。我认为这里的主要问题是IN 检查指定集合中的成员资格,但没有对UPDATE 赋予任何排序,这反过来意味着没有赋予锁定排序的具体排序。

      UPDATE 语句中的WHERE 子句本质上与SELECT 中的行为方式相同。例如,我会经常使用SELECT 模拟UPDATE,以检查将更新的内容是否符合我的预期。

      考虑到这一点,以下使用SELECT 的示例表明IN 本身并不赋予排序:

      鉴于此架构/数据:

      create table foo
      (
        id serial,
        val text
      );
      
      insert into foo (val)
      values ('one'), ('two'), ('three'), ('four');
      

      以下查询:

      select *
      from foo
      where id in (1,2,3,4);
      
      
      select *
      from foo
      where id in (4,3,2,1);
      

      产生完全相同的结果——行从id 1-4 开始。

      即使这不是保证,因为我没有在选择中使用ORDER BY。相反,如果没有它,Postgres 会使用服务器决定最快的任何顺序(参见 Postgres SELECT 文档中关于 ORDER BY 的第 8 点)。给定一个相当静态的表,它通常与插入的顺序相同(就像这里的情况一样)。但是,并不能保证这一点,而且如果表中存在大量流失(大量死元组、删除的行等),则不太可能出现这种情况。

      我怀疑这就是您的UPDATE 所发生的情况。有时——即使不是大部分时间——如果这与插入行的方式相同,它可能会以数字顺序结束,但没有什么可以保证,并且您看到死锁的情况可能是数据的情况已更改,使得一个更新的顺序与另一个不同。

      sqlfiddle 上面的代码。

      可能的修复/解决方法:

      就您可以采取的措施而言,有多种选择,具体取决于您的要求。您可以在表上显式取出表锁,尽管这当然会产生序列化更新的效果,这可能会成为太大的瓶颈。

      另一个仍然允许并发的选项是使用动态 SQL 在例如 Python 中显式迭代项目。这样,您将拥有一组始终以相同顺序发生的单行更新,并且由于您可以确保一致的顺序,正常的 Postgres 锁定应该能够处理并发,而无需死锁。

      这不会像纯 SQL 中的批量更新那样执行,但它应该可以解决锁定问题。提高性能的一个建议是每隔一段时间只使用COMMIT,而不是在每一行之后——这样可以节省大量开销。

      另一种选择是在 PL/pgSQL 中编写的 Postgres 函数中执行循环。然后可以在外部调用该函数,例如在 Python 中,但循环将在服务器端(也明确地)完成,这可能会节省一些开销,因为循环和 UPDATEs 是完全在服务器端完成,无需在每次循环迭代中走线。

      【讨论】:

      • 不幸的是,PostgreSQL 没有(还)提供UPDATE ... ORDER BY,这是我们需要保证的。
      • @Craig:不幸的范围有限,因为子查询中的SELECT .. ORDER BY .. FOR UPDATE 实现了同样的效果。
      • @ErwinBrandstetter 是的,因为 LockRows 节点出现在 Sort 节点之外,这应该没问题 :-) 。尽管如此,用户必须处理这个问题是一种痛苦。
      • @Craig:并不是说UPDATE(和DELETE)的ORDER BY 子句不受欢迎......
      • 感谢您的详细分析!
      猜你喜欢
      • 2020-06-25
      • 2013-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多