事务中的 TRUNCATE:死锁

gue*_*tli 6 postgresql deadlock

在单元测试中,我们运行:

BEGIN;
TRUNCATE table1;
TRUNCATE table2;
...
UPDATE ...;
ROLLBACK;
Run Code Online (Sandbox Code Playgroud)

不幸的是,如果 cron 作业启动并且该作业在 table1 和 table2 上运行,这会导致死锁。

DatabaseError: deadlock detected
DETAIL:  Process 15815 waits for AccessExclusiveLock on relation 291262 of database 290999; blocked by process 16391.
Process 16391 waits for RowShareLock on relation 291431 of database 290999; blocked by process 15815.
Run Code Online (Sandbox Code Playgroud)

有没有办法将单元测试与 cron 作业隔离(以避免死锁)?

我们在 x86_64 linux 上运行 PostgreSQL 9.0.10

Dan*_*ité 6

TRUNCATE仅当确定没有其他会话同时使用该表时才应使用。否则DELETE应该使用。它较慢(如果行数很大),但它旨在支持当前隔离模式规则内的并发访问。

文档说:

TRUNCATE 在它操作的每个表上获取一个 ACCESS EXCLUSIVE 锁,这会阻止该表上的所有其他并发操作。当指定 RESTART IDENTITY 时,任何要重新启动的序列同样被独占锁定。如果需要对表进行并发访问,则应改用 DELETE 命令。

如果DELETE的缓慢是不可接受的,为了 100% 确定地避免死锁,您需要将整个序列放入关键部分,无论是在单元测试中还是在 cron 作业中。通过在任何其他操作之前请求显式锁定来进入临界区,例如:

BEGIN
 SELECT pg_advisory_xact_lock(1); -- (1 is arbitrary: choose any value reserved for this purpose by your apps)
 TRUNCATE ...;
 TRUNCATE ...;
 ...processing...
ROLLBACK;
Run Code Online (Sandbox Code Playgroud)