Postgres 是否自动锁定查询中的所有行,甚至通过 JOIN 跨不同的表?
Does Postgres lock all rows in a query atomically, even across different tables via JOIN?
我的代码出现死锁错误。问题是这个死锁错误发生在事务的第一个查询上。这个查询连接了两个表,TableA
和 TableB
并且应该用 id==table_a_id
锁定 TableA
中的一行,以及 TableB
上的所有具有外部table_a_id
.
键
查询如下所示(我使用的是 SQLAlchemy,此输出来自打印它的等效查询,下面也有它的代码):
SELECT TableB.id AS TableB_id
FROM TableA JOIN TableB ON TableA.id = TableB.table_a_id
WHERE TableB.id = %(id_1)s FOR UPDATE
查询在 SQLAlchemy 语法中如下所示:
query = (
database.query(TableB.id)
.select_from(TableA)
.filter_by(id=table_a_id)
.join((TableB, TableA.id == TableB.table_a_id))
.with_for_update()
)
return query.all()
我的问题是,这个查询会自动锁定两个表中的所有行吗?如果是这样,为什么我会在这个查询上出现死锁,因为它是事务的第一个查询?
查询将在行被选中时一个接一个地锁定它们。确切的顺序将取决于执行计划。也许您可以添加 FOR UPDATE OF table_name
以仅在需要锁定的 table 中锁定行。
我还有两个想法:
重写查询,使其按特定顺序锁定行:
WITH b AS MATERIALIZED (
SELECT id, table_a_id
FROM tableb
WHERE id = 42
FOR NO KEY UPDATE
)
SELECT b.id
FROM tablea
WHERE EXISTS (SELECT 1 FROM b
WHERE tablea.id = b.table_a_id)
ORDER BY tablea.id
FOR NO KEY UPDATE;
性能可能没有那么好,但如果大家都这样选择,就不会出现死锁。
锁定 tables:
LOCK TABLE tablea, tableb IN EXCLUSIVE MODE;
该锁将防止并发行锁和数据修改,因此不会发生死锁。
仅作为 last-ditch 的努力来执行此操作,并且不要经常执行此操作。如果您经常像这样使用高 table 锁,您就会使 autovacuum 远离 运行 并危及数据库的健康。
我的代码出现死锁错误。问题是这个死锁错误发生在事务的第一个查询上。这个查询连接了两个表,TableA
和 TableB
并且应该用 id==table_a_id
锁定 TableA
中的一行,以及 TableB
上的所有具有外部table_a_id
.
查询如下所示(我使用的是 SQLAlchemy,此输出来自打印它的等效查询,下面也有它的代码):
SELECT TableB.id AS TableB_id
FROM TableA JOIN TableB ON TableA.id = TableB.table_a_id
WHERE TableB.id = %(id_1)s FOR UPDATE
查询在 SQLAlchemy 语法中如下所示:
query = (
database.query(TableB.id)
.select_from(TableA)
.filter_by(id=table_a_id)
.join((TableB, TableA.id == TableB.table_a_id))
.with_for_update()
)
return query.all()
我的问题是,这个查询会自动锁定两个表中的所有行吗?如果是这样,为什么我会在这个查询上出现死锁,因为它是事务的第一个查询?
查询将在行被选中时一个接一个地锁定它们。确切的顺序将取决于执行计划。也许您可以添加 FOR UPDATE OF table_name
以仅在需要锁定的 table 中锁定行。
我还有两个想法:
重写查询,使其按特定顺序锁定行:
WITH b AS MATERIALIZED ( SELECT id, table_a_id FROM tableb WHERE id = 42 FOR NO KEY UPDATE ) SELECT b.id FROM tablea WHERE EXISTS (SELECT 1 FROM b WHERE tablea.id = b.table_a_id) ORDER BY tablea.id FOR NO KEY UPDATE;
性能可能没有那么好,但如果大家都这样选择,就不会出现死锁。
锁定 tables:
LOCK TABLE tablea, tableb IN EXCLUSIVE MODE;
该锁将防止并发行锁和数据修改,因此不会发生死锁。
仅作为 last-ditch 的努力来执行此操作,并且不要经常执行此操作。如果您经常像这样使用高 table 锁,您就会使 autovacuum 远离 运行 并危及数据库的健康。