django select_for_update 获取关系锁

django select_for_update acquires relation lock

我有这个代码示例应该在 postgres 中使用行(元组)锁,但是它似乎需要 table(关系)锁:

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();

据我所知,我应该在锁类型中看到“元组”,因为我只锁定特定的 row/s 而不是整个 table

要事第一

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

  • record = MyModel.objects.select_for_update().filter(pk='1234')returns一个QuerySet,没有执行任何查询。
    • record.delete() 只执行一个 DELETE 命令。
  • 一个 SELECT FOR UPDATE 查询会获得一个 relation RowShareLock
    • 您可以通过使用 .first() 执行 QuerySet 来验证这一点,即 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:

Although tuples are a lockable type of object, information about row-level locks is stored on disk, not in memory, and therefore row-level locks normally do not appear in this view. If a transaction is waiting for a row-level lock, it will usually appear in the view as waiting for the permanent transaction ID of the current holder of that row lock.

来自https://www.postgresql.org/docs/9.4/explicit-locking.html

FOR UPDATE

...

The FOR UPDATE lock mode is also acquired by any DELETE on a row ...

您可以在您的 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; 无效。

Table 级(关系)锁

  1. relation AccessShareLock 似乎是为您未在代码示例中显示的 SELECT 查询获取的,例如MyModel.objects.filter(pk='1234').first().
  2. relation RowExclusiveLockDELETE 命令获取。

虽然这些是table级别的锁,但它们只与EXCLUSIVE and/or ACCESS EXCLUSIVE锁冲突,大多数其他DQL(数据查询语言)不会获取这些锁) 和 DML(数据操作语言)命令。

来自https://www.postgresql.org/docs/9.4/explicit-locking.html

ACCESS SHARE

Conflicts with the ACCESS EXCLUSIVE lock mode only.

The SELECT command acquires a lock of this mode on referenced tables. In general, any query that only reads a table and does not modify it will acquire this lock mode.

ROW EXCLUSIVE

Conflicts with the EXCLUSIVE and ACCESS EXCLUSIVE lock modes.

The commands UPDATE, DELETE, and INSERT acquire this lock mode on the target table (in addition to ACCESS SHARE locks on any other referenced tables). In general, this lock mode will be acquired by any command that modifies data in a table.