我遇到“因并发更新无法序列化访问”的问题。我检查了日志,我可以清楚地看到两个交易试图同时更新一行。
我的SQL查询
UPDATE sessionstore SET valid_until = %s WHERE sid = %s;
如何告诉postgres“尝试”更新行而不抛出任何异常?
答案 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来简化问题的原因,然后任何实际的错误都是真正的意外。