由于并发更新,Postgres无法序列化访问

时间:2018-06-11 11:52:19

标签: sql postgresql

我遇到“因并发更新无法序列化访问”的问题。我检查了日志,我可以清楚地看到两个交易试图同时更新一行。

我的SQL查询

UPDATE sessionstore SET valid_until = %s WHERE sid = %s;

如何告诉postgres“尝试”更新行而不抛出任何异常?

2 个答案:

答案 0 :(得分:0)

这样做的规范方法是在UPDATE之前设置一个检查点:

SAVEPOINT x;

如果更新失败,

ROLLBACK TO SAVEPOINT x;

,交易可以继续。

答案 1 :(得分:0)

这里有一个警告,已在注释中提及。您必须使用REPEATABLE READ事务隔离或更高版本。为什么?除非您确实有特定原因,否则通常不需要这样做。

如果您使用标准的READ COMMITTED,您的问题将消失。但是,最好还是使用“跳过锁定”来避免锁定等待和冗余更新以及浪费的WAL流量。

从Postgres 9.5+开始,有一种更好的方法来处理此问题,就像这样:

UPDATE sessionstore
SET valid_until = %s
WHERE sid = (
    SELECT sid FROM sessionstore
    WHERE sid = %s
    FOR UPDATE SKIP LOCKED
);

第一个在SELECT FOR UPDATE SKIP LOCKED中获得锁定的事务将导致任何冲突的事务都不选择任何内容,从而导致无操作。根据要求,它不会引发异常。

请参阅此处的“跳过锁定”注释: https://www.postgresql.org/docs/current/static/sql-select.html

关于保存点的建议还不够具体。如果更新由于序列化错误以外的原因导致失败怎么办?像实际的僵局一样?您不想只是默默地忽略所有错误。但这通常也是个坏主意-异常处理程序或每行更新的保存点会带来很多额外的开销,尤其是在流量较高的情况下。这就是为什么您应该同时使用READ COMMITTED和SKIP LOCKED来简化问题的原因,然后任何实际的错误都是真正的意外。