gke*_*rus 6 postgresql deadlock locking
正如在stackoverflow 上被问到的那样,但是对于 MySQL,我想知道它在 PostgreSQL 中是如何工作的。当我将“FOR UPDATE”与“LIMIT”、“ORDER BY”和一些“WHERE”组合在一起时,锁定了多少行。他们更新返回的行。我希望“FOR UPDATE”查询只会锁定一行,但也许我错过了一些实现问题。(我打算写一些压力测试来更可靠地重现这一点)
这些查询导致我的系统出现死锁:
(session1) select field1, field2, ... from documents where transaction_path='some/path' for update
(session2) select field1, field2, ... from documents where (transaction_path is null) and queue_id=2 and next_pickup_ts<now() order by next_pickup_ts limit 1 for update
(session3) select field1, field2, ... from documents where (transaction_path is null) and queue_id=2 and next_pickup_ts<now() order by next_pickup_ts limit 1 for update
Run Code Online (Sandbox Code Playgroud)
但我不明白为什么。(transaction_path 上有一个唯一索引。)
如果您使用,EXPLAIN
您将看到排序。对于我用一些一次性数据和类似查询 ( ORDER BY ... LIMIT 1 FOR UPDATE
)炮制的随机计划,我得到了计划:
Limit (cost=33.09..33.10 rows=1 width=26)
-> LockRows (cost=33.09..39.88 rows=543 width=26)
-> Sort (cost=33.09..34.45 rows=543 width=26)
Sort Key: t_id
-> Seq Scan on m (cost=0.00..30.38 rows=543 width=26)
Filter: (b_id <= 33)
(6 rows)
Run Code Online (Sandbox Code Playgroud)
在这里,您可以看到在应用限制之前行可能被锁定。
现在,在实践中,匹配的第一行通常会被锁定并返回,导致LockRows
后面的行被跳过……但不能保证这一点。所以你不应该依赖它。
一般来说,混合行锁定和LIMIT
是一个坏主意,通常应该避免。
如果你必须这样做,你会想要做这样的事情:
SELECT *
FROM my_table
WHERE id = (SELECT id FROM my_table
WHERE mywhereclause
ORDER BY ... LIMIT 1)
AND mywhereclause
FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
您强制找到目标行,然后才锁定。
您应该在外部查询中重复 WHERE 子句,以确保在发生任何锁定等待后该行仍然与谓词匹配。否则可能发生的情况是内部查询找到该行,返回 ID,然后您尝试使用该 ID 锁定该行。PostgreSQL 看到其他人对该行有锁,因此它等待该锁被释放。然后它重新检查WHERE
外部查询上的子句以确保它仍然匹配(以防该行被删除或被删除UPDATE
)。因为内部查询是一个不相关的子查询,所以它不会重新计算它,所以内部WHERE
子句不会重新运行,WHERE
如果你不重复,你可以获得与返回的内部子句不再匹配的行它在外部WHERE
条款中。
混合行限制和行锁定很困难。构建实际上比单个工人表现更好的正确排队系统甚至更难。使用现成的排队系统并为自己省去很多麻烦。
(PostgreSQL 9.5 将有SKIP LOCKED
,这使得这种方式更容易,但从现在起一年后不会有稳定的版本,所以不要屏住呼吸。)
归档时间: |
|
查看次数: |
4716 次 |
最近记录: |