Postgres,更新和锁定订单

sea*_*and 4 sql postgresql deadlock locking transactions

我正在研究Postgres 9.2.

有2个UPDATE,每个都在自己的交易中.一个看起来像:

UPDATE foo SET a=1 WHERE b IN (1,2,3,4);
Run Code Online (Sandbox Code Playgroud)

另一个是类似的:

UPDATE foo SET a=2 WHERE b IN (1,2,3,4);
Run Code Online (Sandbox Code Playgroud)

这些可能同时运行,实际上在'IN'表达式中有500+.我有时会看到死锁.确实,"IN"表达式中的项目顺序实际上可能不会影响真正的锁定顺序吗?

Erw*_*ter 11

有没有ORDER BYUPDATE命令.
但是有SELECT.对子查询中的子句使用行级锁定:FOR UPDATE

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;
Run Code Online (Sandbox Code Playgroud)

当然,b必须UNIQUE或者您需要在ORDER BY子句中添加更多表达式以使其明确无误.

而你需要强制执行的相同顺序所有 UPDATE,DELETESELECT .. FOR UPDATE在表的语句.

相关,更多细节:


kha*_*son 6

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

WHERE在第UPDATE陈述行为基本上它在一个同样的方式SELECT.例如,我经常会模拟UPDATE使用a SELECT来检查将要更新的内容,以确定它是我所期望的.

考虑到这一点,下面的示例使用SELECT演示IN本身不会赋予顺序:

鉴于此架构/数据:

create table foo
(
  id serial,
  val text
);

insert into foo (val)
values ('one'), ('two'), ('three'), ('four');
Run Code Online (Sandbox Code Playgroud)

以下查询:

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


select *
from foo
where id in (4,3,2,1);
Run Code Online (Sandbox Code Playgroud)

产生完全相同的结果 - 行从id1-4开始.

即使这样也无法保证,因为我没有ORDER BY在选择中使用.相反,没有它,Postgres使用服务器决定的最快的顺序(参见Postgres SELECT doc ORDER BY中的第8点).给定一个相当静态的表,它通常与插入的顺序相同(就像这里的情况一样).然而,没有任何保证,如果桌子上有很多流失(许多死元组,行删除等),那么情况就不太可能了.

我怀疑这是你在这里发生的事情UPDATE.有时 - 如果不是大多数时候 - 如果插入行的方式相同,它可能以数字顺序结束,但没有什么可以保证的,而且你看到死锁的情况可能是数据的情况已更改,以使一个更新的顺序与另一个更新.

sqlfiddle与上面的代码.

可能的修复/解决方法:

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

另一种选择,这将仍然允许并发-是一种通过使用动态的项目明确迭代SQL,比方说,Python的.这样,您将拥有一组始终以相同顺序发生的一行更新,并且由于您可以确保一致的顺序,因此正常的Postgres锁定应该能够处理并发而不会发生死锁.

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

另一种选择是在用PL/pgSQL编写的Postgres函数中进行循环.然后可以在Python中外部调用该函数,但是循环将在服务器端完成(也明确地),这可以节省一些开销,因为循环完全是在服务器端完成而不必过去电线每次循环迭代.UPDATEs

  • 不幸的是,PostgreSQL还没有(还)提供一个`UPDATE ... ORDER BY`,这是我们需要保证的. (2认同)
  • @Craig:由于子查询中的`SELECT .. ORDER BY .. FOR UPDATE`达到了相同的目的,因此不幸的范围有限. (2认同)