django select_for_update 获取关系锁
Posted
技术标签:
【中文标题】django select_for_update 获取关系锁【英文标题】:django select_for_update acquires relation lock 【发布时间】:2021-12-20 09:45:23 【问题描述】:我有这个代码示例应该在 postgres 中使用行(元组)锁定,但它似乎改为使用表(关系)锁定:
with transaction.Atomic(savepoint=True, durable=False):
record = MyModel.objects.select_for_update().filter(pk='1234')
record.delete()
time.sleep(5)
raise Exception
通过在事务期间查看 pg_locks 我可以看到:
select locktype, database, relation::regclass, pid, mode, granted from pg_locks where pid <> pg_backend_pid();
据我所知,我应该在 locktype 中看到“元组”,因为我只锁定特定行而不是整个表
【问题讨论】:
【参考方案1】:首要任务
您实际上没有执行SELECT FOR UPDATE
查询。
record = MyModel.objects.select_for_update().filter(pk='1234')
返回QuerySet
,不执行查询。
record.delete()
只执行DELETE
命令。
SELECT FOR UPDATE
查询将获得relation
RowShareLock
。
您可以通过执行QuerySet
和.first()
来验证这一点,即record = MyModel.objects.select_for_update().filter(pk='1234').first()
。
您可以通过log all sql queries 验证这一点。
行级(元组)锁
获取了行级FOR UPDATE
锁,但未显示在您的pg_locks
视图中(它也未显示在我的视图中)。相反,我们看到的是transactionid
ExclusiveLock
(和virtualxid
ExclusiveLock
)。
来自https://www.postgresql.org/docs/9.3/view-pg-locks.html:
虽然元组是一种可锁定类型的对象,但有关行级锁的信息存储在磁盘上,而不是内存中,因此行级锁通常不会出现在此视图中。如果事务正在等待行级锁,它通常会在视图中显示为等待该行锁当前持有者的永久事务 ID。
来自https://www.postgresql.org/docs/9.4/explicit-locking.html:
FOR UPDATE
...
FOR UPDATE
锁定模式也被任何DELETE
连续获取...
您可以通过在 psql 终端中运行来凭经验验证这一点:
之前record.delete()
SELECT FROM mymodel WHERE id='1' FOR UPDATE;
有效。
SELECT FROM mymodel WHERE id='1234' FOR UPDATE;
有效。
在record.delete()
之后
SELECT FROM mymodel WHERE id='1' FOR UPDATE;
有效。
SELECT FROM mymodel WHERE id='1234' FOR UPDATE;
不起作用。
表级(关系)锁
relation
AccessShareLock
似乎是为您未在代码示例中显示的 SELECT
查询获取的,例如MyModel.objects.filter(pk='1234').first()
。
relation
RowExclusiveLock
是为 DELETE
命令获取的。
虽然这些是表级锁,但它们只与EXCLUSIVE
和/或ACCESS EXCLUSIVE
锁发生冲突,大多数其他 DQL(数据查询语言)和 DML(数据操作语言)命令都不会获取这些锁。
来自https://www.postgresql.org/docs/9.4/explicit-locking.html:
ACCESS SHARE
仅与
ACCESS EXCLUSIVE
锁定模式冲突。
SELECT
命令在引用的表上获取此模式的锁。一般来说,任何只读取表而不修改表的查询都会获取这种锁定模式。
ROW EXCLUSIVE
与
EXCLUSIVE
和ACCESS EXCLUSIVE
锁定模式冲突。命令
UPDATE
、DELETE
和INSERT
在目标表上获取此锁定模式(除了ACCESS SHARE
对任何其他引用表的锁定)。一般情况下,任何修改表中数据的命令都会获得这种锁模式。
【讨论】:
感谢@aaron 的解释。事实上,我更改了 sn-p,因此您看不到 AccessShareLock。试想一下,我有 get() 而不是 filter()... 总而言之,根据您的写作,我实际上同时有两个不同的锁级别。第一个在行上(我在 pg_locks 中看不到),而第二个在表上(作为“RowExclusiveLock”)? 是的,没错。以上是关于django select_for_update 获取关系锁的主要内容,如果未能解决你的问题,请参考以下文章
django select_for_update 获取关系锁
为啥不支持它的数据库可以简单地忽略 select_for_update?