2

我有这个代码示例应该在 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 中看到“元组”,因为我只锁定特定行而不是整个表

4

1 回答 1

1

第一件事

您实际上没有执行SELECT FOR UPDATE查询。

  • record = MyModel.objects.select_for_update().filter(pk='1234')返回 a QuerySet,不执行任何查询。
    • record.delete()只执行一个DELETE命令。
  • SELECT FOR UPDATE查询将获得一个relation RowShareLock.
    • 您可以通过执行QuerySetwith来验证这一点.first(),即record = MyModel.objects.select_for_update().filter(pk='1234').first()
  • 您可以通过记录所有 sql 查询来验证这一点。

行级(元组)锁

获取了行级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;不起作用。

表级(关系)锁

  1. relation AccessShareLock似乎是为SELECT您未在代码示例中显示的查询获取的,例如MyModel.objects.filter(pk='1234').first().
  2. 为命令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锁定模式冲突。

命令UPDATEDELETEINSERT在目标表上获取此锁定模式(除了ACCESS SHARE任何其他引用表上的锁定)。一般来说,任何修改表中数据的命令都会获取这种锁定模式。

于 2021-11-07T06:08:23.637 回答