PostgreSQL 教程: 检查操作采用的锁级别

九月 25, 2024

摘要:在本教程中,您将学习如何检查 PostgreSQL 中的操作所采用的锁级别。

目录

介绍

在 PostgreSQL 的文档中,通常对各种操作采用的锁都有清楚的说明 - 但没有什么是完美的。如果需要,您也可以直接询问 PostgreSQL。

您可以使用psql或 PgAdmin 轻松检查锁定级别。

例如,要查看alter table some_table disable trigger some_trigger采用什么锁,可以这样:

BEGIN;

ALTER TABLE some_table DISABLE TRIGGER some_trigger;

SELECT locktype, mode FROM pg_locks
  WHERE pid = pg_backend_pid() AND relation = 'some_table'::regclass;
 locktype |         mode
----------+-----------------------
 relation | ShareRowExclusiveLock
(1 row)

那是在一个表上的一个锁。从中可以很容易地看出,我们采用了一个SHARE ROW EXCLUSIVE锁,根据文档:

… 保护表免受并发数据更改的影响,并且对自身也是排他性的,因此一次只有一个会话可以持有它。

pg_locks 的其他用途

您还可以通过其他方式对pg_locks进行过滤,以查看事务持有的其他锁。你必须和pg_class等系统表做一些连接,来解析 OID 与名称的关系 – 这正是为什么我们真的需要 PostgreSQL 中有一个pg_stat_locks视图,来简化这个过程。

人们会预先编写各种质量参差不齐的的查询,来查看目前哪些进程阻止了其他进程。在 PostgreSQL 9.6 以后的版本中,随着添加更详细的锁等待信息,和新增pg_blocking_pids()函数,这也变得要容易多了,尽管它还没有添加一个辅助视图。

什么是 virtualxid 和 transactionid 锁?

pg_locks中,您会看到的一件重要且可能令人困惑的事情是,每个事务都持有一个特殊的锁,称为virtualxid锁:

BEGIN;

SELECT * FROM pg_locks WHERE pid = pg_backend_pid();
  locktype  | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction |  pid  |      mode       | granted | fastpath 
------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-------+-----------------+---------+----------
 relation   |    16386 |    11673 |      |       |            |               |         |       |          | 2/3983             | 24250 | AccessShareLock | t       | t
 virtualxid |          |          |      |       | 2/3983     |               |         |       |          | 2/3983             | 24250 | ExclusiveLock   | t       | t
(2 rows)
select '11673'::regclass;
 regclass 
----------
 pg_locks
(1 row)

正如你所看到的,关系的 AccessShareLock 正是我们在查询pg_locks时在其上面采用的锁,所以你可以忽略它。

virtualxid锁很特殊。它是一个在事务自己的虚拟事务 ID(上面的 “2/3983”)上的排他锁,每个事务始终会持有该锁。当事务正在运行时,任何其他事务都无法获取它。这样做的目的是,允许一个事务使用 PostgreSQL 的锁定机制,等待另一个事务提交或回滚,这只会在内部使用。您通常不需要自己使用它,但当您在pg_locks中看到它时,了解它是什么会很有用。

对于获得实际读/写事务 ID 的事务,会有一个类似的条目,其他事务可以使用它来等待它们提交或回滚:

select txid_current();
 txid_current
--------------
         2774
(1 row)

SELECT * FROM pg_locks WHERE pid = pg_backend_pid();
   locktype    | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction |  pid  |      mode       | granted | fastpath
---------------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-------+-----------------+---------+----------
 relation      |    16386 |    11673 |      |       |            |               |         |       |          | 2/3984             | 24250 | AccessShareLock | t       | t
 virtualxid    |          |          |      |       | 2/3984     |               |         |       |          | 2/3984             | 24250 | ExclusiveLock   | t       | t
 transactionid |          |          |      |       |            |          2774 |         |       |          | 2/3984             | 24250 | ExclusiveLock   | t       | f
(3 rows)

所以,如果你以前想知道它们是什么,现在你知道了。

当两个并发事务尝试将相同的键值插入一个唯一索引(或主键)时,您会看到一个transactionid的锁等待。在这种情况下,第二个事务必须等到第一个提交或回滚,才能知道它是应该失败并出现错误,还是可以继续插入。

了解更多

PostgreSQL 监控