Postgres,更新和锁定订单

时间:2014-12-03 03:00:31

标签: sql postgresql transactions locking deadlock

我正在使用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'表达。 我有时会看到死锁。是的,' IN'中的项目顺序是真的。表达式实际上可能不会影响真正的锁定顺序吗?

2 个答案:

答案 0 :(得分:10)

ORDER BY命令中没有UPDATE 但是有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子句中添加更多表达式,以使其明确无误。

您需要对表格中所有 UPDATEDELETESELECT .. FOR UPDATE语句执行相同的订单。

相关,更多细节:

答案 1 :(得分:6)

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

WHERE语句中的UPDATE子句的行为基本上与SELECT中的UPDATE子句相同。例如,我会经常使用SELECT来模拟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

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

即使这不是保证,因为我在select中没有使用ORDER BY。相反,没有它, Postgres 使用服务器决定最快的顺序(参见Postgres SELECT doc中关于UPDATE的第8点)。给定一个相当静态的表,它通常与插入的顺序相同(就像这里的情况一样)。然而,没有任何保证,如果桌面上有大量的流失(许多死元组,删除的行等),那么它的可能性就会降低。< / p>

我怀疑你的COMMIT发生了什么。有时 - 如果不是大多数时候 - 如果插入行的方式相同,它可能以数字顺序结束,但是没有什么可以保证的,以及你看到的情况死锁可能是数据发生变化的情况,因此一个更新的排序与另一个不同。

sqlfiddle上面的代码。

可能的修复/解决方法:

就您可以采取的措施而言,根据您的要求,有多种选择。你可以在表上明确地取出一个表锁,虽然那当然会产生序列化那里的更新的效果,这可能被证明是一个太大的瓶颈。

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

它在纯SQL中的表现不如批量更新,但它应该解决锁定问题。提高性能的一个建议是每隔一段时间只UPDATEs,而不是每一行 - 这可以节省大量开销。

另一个选择是在 PL / pgSQL 中编写的 Postgres 函数中执行循环。然后可以在外部调用该函数,例如 Python ,但是循环将在服务器端完成(也明确),这可以节省一些开销,因为循环和{{1}完全是在服务器端完成的,而不必在每次循环迭代时都通过电线。