使用UPDATE FOR和外键锁定postgres

时间:2014-03-10 19:01:39

标签: python sql postgresql concurrency sqlalchemy

我的Postgres表:

表演

  • ID
  • group_id REFERENCES群组

性能

  • ID
  • 付费
  • performer_id REFERENCES表演者

  • ID

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

我看到很多错误的例子(在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个。{/ p>

我该如何解决这个问题?我可以切换到不同的事务锁定级别吗?我宁愿不只是中止而且必须重试 - 我希望数据库能够为我解决这个问题。

必须有某种方法来解决这个问题 - 我想做的事情非常简单。

1 个答案:

答案 0 :(得分:2)

如果所有并发写入操作都以相同的唯一顺序进行,则可以避免死锁。将ORDER BY添加到锁定语句中,并在所有位置使用相同的排序顺序。类似的东西:

SELECT ... FROM performers WHERE ... ORDER BY performers.id FOR UPDATE

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

此外,如果您的操作涉及多个列,请在表中严格按照相同的顺序进行锁定。

this thread in pgsql-general list中的更多详情 Or in the manual

将触发器(和外键约束)保持在必要的最小值。无论哪种方式,首先要排序FOR UPDATE锁。这可能只是解决你的问题。如果没有,请考虑不同的transaction isolation levelSerializable应该做到这一点。但是,必须准备好重试交易,直到它们成功为止。