九月 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
的锁等待。在这种情况下,第二个事务必须等到第一个提交或回滚,才能知道它是应该失败并出现错误,还是可以继续插入。