我遇到了“由于并发更新而无法序列化访问”的问题。我检查了日志,我可以清楚地看到两个事务试图同时更新一行。
我的 sql 查询
UPDATE sessionstore SET valid_until = %s WHERE sid = %s;
Run Code Online (Sandbox Code Playgroud)
如何告诉 postgres “尝试”更新行而不抛出任何异常?
这里有一个警告,已在评论中提到。您必须使用 REPEATABLE READ 事务隔离或更高级别的隔离。为什么?除非您确实有特定原因,否则通常不需要这样做。
\n\n如果您使用标准的 READ COMMITTED,您的问题就会消失。但仍然最好使用 SKIP LOCKED 来避免锁等待和冗余更新以及浪费的 WAL 流量。
\n\n从 Postgres 9.5+ 开始,有一个更好的方法来处理这个问题,如下所示:
\n\nUPDATE sessionstore\nSET valid_until = %s\nWHERE sid = (\n SELECT sid FROM sessionstore\n WHERE sid = %s\n FOR UPDATE SKIP LOCKED\n);\nRun Code Online (Sandbox Code Playgroud)\n\n在 SELECT FOR UPDATE SKIP LOCKED 中获取锁的第一个事务将导致任何冲突事务不选择任何内容,从而导致无操作。按照要求,它不会抛出异常。
\n\n请参阅此处的“跳过锁定”注释:\n https://www.postgresql.org/docs/current/static/sql-select.html
\n\n此外,关于保存点的建议不够具体。如果更新因序列化错误以外的原因而失败怎么办?就像真正的僵局一样?您不想\xe2\x80\x99 只想默默地忽略所有错误。但这些通常也是一个坏主意 - 每行更新的异常处理程序或保存点都会产生大量额外开销,特别是在流量较高的情况下。这就是为什么您应该使用 READ COMMITTED 和 SKIP LOCKED 来简化问题,并且任何实际错误都将是真正意外的。
\n执行此操作的规范方法是在以下操作之前设置一个检查点UPDATE:
SAVEPOINT x;
Run Code Online (Sandbox Code Playgroud)
如果更新失败,
ROLLBACK TO SAVEPOINT x;
Run Code Online (Sandbox Code Playgroud)
并且交易可以继续进行。
| 归档时间: |
|
| 查看次数: |
16157 次 |
| 最近记录: |