在Postgres中优化并发更新


9

我正在运行并发Postgres查询,如下所示:

UPDATE foo SET bar = bar + 1 WHERE baz = 1234

每个查询都会影响固定的K个行数,而且我找不到一种方法来强制更新行的顺序,最终导致死锁。当前,我通过手动执行顺序来解决此问题,但这意味着我必须执行比平时更多的查询,同时还将搜索复杂度从O(log N + K)提升到O(K log N)。

有没有一种方法可以提高性能而又不会导致死锁?我怀疑只要Postgres以与扫描行相同的顺序更新行,就可以用(baz)索引替换索引(baz, id),这是值得追求的方法吗?


我建议您添加CREATE TABLE代码。
ypercubeᵀᴹ

Answers:


15

有没有ORDER BYSQL UPDATE命令。Postgres以任意顺序更新行:

为了绝对避免死锁,您可以在可序列化事务隔离中运行语句。但这更昂贵,您需要准备在序列化失败时重复命令。

最好的做法可能是SELECT ... ORDER BY ... FOR UPDATE在子查询中显式锁定,或者SELECT在事务中独立使用-默认为“读取已提交”隔离级别。在pgsql-general上引用Tom Lane

应该没问题--- FOR UPDATE锁定始终是SELECT管道中的最后一步。

这应该做的工作:

BEGIN;

SELECT 1
FROM   foo 
WHERE  baz = 1234
ORDER  BY bar
FOR    UPDATE;

UPDATE foo
SET    bar = bar + 1
WHERE  baz = 1234;

COMMIT;

上的多列索引(baz, bar)可能是性能的理想选择。但是由于bar显然已更新很多,因此单列索引(baz)可能更好。取决于几个因素。每行多少行baz?没有多列索引,是否可以进行HOT更新?...

如果 baz同时更新,则冲突的可能性极小(根据文档)

SELECT命令可能在READ COMMITTED 事务隔离级别运行,并使用ORDER BY和锁定子句可能会无序返回行。...

另外,如果您应具有涉及的唯一约束bar,请考虑DEFERRABLE在同一命令中避免唯一冲突约束。相关答案:


1
如果我要按id或其他唯一的列而不是进行排序bar,则不应出现极端情况或性能下降,对吗?
Alexei Averchenko 2014年

@AlexeiAverchenko:是的,一个从未更新的唯一列对此非常适合-并且多列索引(包括此列在第二位置)。
Erwin Brandstetter 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.