【问题标题】:postgres locking with UPDATE FOR and foreign keys使用 UPDATE FOR 和外键锁定 postgres
【发布时间】:2014-03-10 19:01:38
【问题描述】:

我的 Postgres 表:

表演者

  • 身份证
  • 排名
  • group_id REFERENCES 组

表演

  • 身份证
  • 支付
  • performer_id 引用表演者

组

  • 身份证

我经常需要从一个组中选择所有表演者并更新他们的所有排名。但这通常会导致死锁,因为我正在执行 SELECT ... FOR UPDATE 选择,而其他线程同时是 INSERTing。

我经常看到的错误示例(在 Python SqlAlchemy 中):

DBAPIError: (TransactionRollbackError) deadlock detected
DETAIL:  Process 83182 waits for ShareLock on transaction 14282922; blocked by process 83171.
Process 83171 waits for ShareLock on transaction 14282925; blocked by process 83182.
HINT:  See server log for query details.
 'SELECT performers.id AS performers_id, performers.rank AS performers_rank, performers.group_id AS performers_group_id \nFROM performers \nWHERE performers.group_id = %(group_id_1)s FOR UPDATE' {'group_id_1': 2}

我也有found 几个examples 这个behavior。

我该如何解决这个问题?我可以切换到不同的事务锁定级别吗?我宁愿不只是中止并不得不重试 - 我希望数据库为我处理这个争用。

一定有办法解决这个问题——我想做的很简单。

【问题讨论】:

  • 插入performances 时,在每个不同的performer_id 之后提交以避免与更新交叉锁定。它是ShareLock 的事实强烈表明它是为 FK 拍摄的。
  • PostgreSQL 发回给客户端的错误信息中缺少其他事务的信息,避免信息泄露给客户端。 PostgreSQL 日志文件对这两个事务有什么说明?

标签: python sql postgresql concurrency sqlalchemy


【解决方案1】:

如果所有并发写入操作都以相同的唯一顺序进行,则可以避免死锁。将ORDER BY 添加到您的锁定语句中,并在任何地方使用相同的排序顺序。比如:

SELECT ... FROM performers WHERE ... <b>ORDER BY performers.id</b> FOR UPDATE

id 将是主键(或任何其他稳定、明确的列组合)。

此外,如果您的操作涉及多个列,请以严格相同的顺序跨表执行锁定。

更多详情请见this thread in pgsql-general list。
Or in the manual。

将触发器(和外键约束)保持在必要的最低限度。不管怎样,先理清FOR UPDATE的锁。这可能只是解决你的问题。如果没有,请考虑使用不同的transaction isolation level。 Serializable 应该可以解决问题。但是你必须准备好重试事务直到它们成功。

【讨论】:

  • 插入呢?这显然不适用于那里,对吗?
  • @lollercoaster:正确。你不能锁定不存在的东西(还)。不过,我不知道 INSERT 会如何死锁?您可能会遇到重复的密钥违规,但这是另一种问题。
  • 是的,当我从不同的线程一次向性能表中插入多个值时,我遇到了死锁——我怀疑是因为外键?我其实不知道为什么。
  • @lollercoaster:是的,外键可能会在你身上做一个数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-29
  • 2021-11-23
  • 1970-01-01
  • 2011-06-18
  • 2016-12-01
  • 2011-06-08
相关资源
最近更新 更多