我正在使用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'中的项目顺序是真的。表达式实际上可能不会影响真正的锁定顺序吗?
答案 0 :(得分:10)
ORDER BY
命令中没有UPDATE
但是有SELECT
。在子查询中使用row-level locking和FOR 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
子句中添加更多表达式,以使其明确无误。
您需要对表格中所有 UPDATE
,DELETE
和SELECT .. 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}完全是在服务器端完成的,而不必在每次循环迭代时都通过电线。