2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4

53.13. pg_locks #

视图 pg_locks 提供对数据库服务器中活动进程所持锁的信息的访问。有关锁的更多讨论,请参阅 第 13 章

pg_locks 包含每个活动的可锁定对象、请求的锁模式以及相关进程的一行。因此,同一个可锁定对象可能会出现多次,如果多个进程正在持有或等待其上的锁。但是,当前没有任何锁的对象将完全不会出现。

有几种不同类型的可锁定对象:整个关系(例如表)、关系的单个页面、关系的单个元组、事务 ID(虚拟和永久 ID)以及通用数据库对象(由类 OID 和对象 OID 标识,方式与 pg_descriptionpg_depend 中的方式相同)。此外,扩展关系的权限被表示为一个独立的可锁定对象,更新 pg_database.datfrozenxid 的权限也是如此。此外,还可以对用户定义的数字进行“advisory”锁。

表 53.13. pg_locks

列 类型

描述

locktype text

可锁定对象的类型:relationextendfrozenidpagetupletransactionidvirtualxidspectokenobjectuserlockadvisoryapplytransaction。(另请参阅 表 27.11。)

database oid(引用 pg_database.oid

锁目标所在的数据库的 OID,如果目标是共享对象则为零,如果目标是事务 ID 则为 NULL。

relation oid(引用 pg_class.oid

锁所针对的关系的 OID,如果目标不是关系或关系的一部分则为 NULL。

page int4

锁所针对的关系中的页面编号,如果目标不是关系页面或元组则为 NULL。

tuple int2

锁所针对的页面中的元组编号,如果目标不是元组则为 NULL。

virtualxid text

锁所针对的事务的虚拟 ID,如果目标不是虚拟事务 ID 则为 NULL;请参阅 第 67 章

transactionid xid

锁所针对的事务的 ID,如果目标不是事务 ID 则为 NULL;第 67 章

classid oid(引用 pg_class.oid

包含锁目标的系统目录的 OID,如果目标不是通用数据库对象则为 NULL。

objid oid(引用任何 OID 列)

其系统目录中的锁目标的 OID,如果目标不是通用数据库对象则为 NULL。

objsubid int2

锁所针对的列号(classidobjid 指的是表本身),如果目标是其他通用数据库对象则为零,如果目标不是通用数据库对象则为 NULL。

virtualtransaction text

持有或正在等待此锁的事务的虚拟 ID。

pid int4

持有或正在等待此锁的服务器进程的进程 ID,如果锁由已准备的事务持有则为 NULL。

mode text

此进程持有或请求的锁模式的名称(请参阅 13.3.1 节13.2.3 节)。

granted bool

如果锁已被授予,则为 True;如果锁正在等待,则为 False。

fastpath bool

如果锁是通过快速路径获取的,则为 True;如果通过主锁表获取的,则为 False。

waitstart timestamptz

服务器进程开始等待此锁的时间,如果锁已被授予则为 NULL。请注意,即使 grantedfalse,在等待开始后的非常短时间内此字段也可能为 NULL。


对于由指示进程持有的锁,granted 为 True。False 表示此进程当前正在等待获取此锁,这意味着至少有一个其他进程正在持有或等待同一可锁定对象上的冲突锁模式。等待进程将睡眠,直到其他锁被释放(或检测到死锁情况)。一个进程一次最多只能等待获取一个锁。

在整个事务运行过程中,服务器进程会持有该事务的虚拟事务 ID 的排他锁。如果为该事务分配了永久 ID(这通常只发生在事务更改了数据库状态时),它还将持有该事务的永久事务 ID 的排他锁,直到事务结束。当进程发现有必要专门等待另一个事务结束时,它会尝试获取其他事务 ID(根据情况是虚拟 ID 还是永久 ID)的共享锁。这只有在其他事务终止并释放其锁时才会成功。

尽管元组是可锁定的对象类型,但有关行级锁的信息存储在磁盘上,而不是内存中,因此行级锁通常不会出现在此视图中。如果进程正在等待行级锁,它通常会在此视图中显示为正在等待该行锁当前持有者的永久事务 ID。

投机插入锁由一个事务 ID 和一个投机插入令牌组成。投机插入令牌显示在 objid 列中。

Advisory 锁可以基于由单个 bigint 值或两个整数值组成的键来获取。一个 bigint 键在其高位部分显示在 classid 列中,其低位部分显示在 objid 列中,objsubid 等于 1。可以使用表达式 (classid::bigint << 32) | objid::bigint 重新组合原始 bigint 值。整数键显示为第一个键在 classid 列中,第二个键在 objid 列中,objsubid 等于 2。键的实际含义由用户决定。Advisory 锁在每个数据库中都是本地的,因此 database 列对于 advisory 锁是有意义的。

Apply transaction 锁用于并行模式以应用逻辑复制中的事务。远程事务 ID 显示在 transactionid 列中。objsubid 显示锁子类型,其中 0 用于同步更改集的锁,1 用于等待事务完成以确保提交顺序的锁。

pg_locks 提供数据库集群中所有锁的全局视图,而不仅仅是与当前数据库相关的锁。尽管其 relation 列可以与 pg_class.oid 进行连接以识别被锁定的关系,但这仅对当前数据库中的关系(即 database 列为当前数据库的 OID 或零的关系)有效。

可以将 pid 列与 pg_stat_activity 视图的 pid 列连接起来,以获取有关持有或等待每个锁的会话的更多信息,例如:

SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa
    ON pl.pid = psa.pid;

此外,如果您正在使用已准备的事务,可以将 virtualtransaction 列与 pg_prepared_xacts 视图的 transaction 列连接起来,以获取有关持有锁的已准备事务的更多信息。(已准备的事务永远不会等待锁,但它会继续持有其在运行期间获取的锁。)例如:

SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
    ON pl.virtualtransaction = '-1/' || ppx.transaction;

虽然可以通过将 pg_locks 与自身连接来获取有关哪些进程阻塞其他进程的信息,但这在细节上很难正确处理。这样的查询必须编码有关哪些锁模式与哪些其他锁模式冲突的知识。更糟糕的是,pg_locks 视图并未显示关于哪些进程在锁等待队列中排在其他进程前面的信息,也没有关于哪些进程是代表其他客户端会话运行的并行工作进程的信息。最好使用 pg_blocking_pids() 函数(请参阅 表 9.71)来识别等待进程正在等待的进程。

视图 pg_locks 显示了来自常规锁管理器和谓词锁管理器的数据,它们是两个独立的系统;此外,常规锁管理器将其锁细分为常规锁和快速路径锁。这些数据不保证完全一致。查询视图时,从每个后端逐个收集快速路径锁(fastpath = true)的数据,而不冻结整个锁管理器的状态,因此在收集信息期间可能存在锁的获取或释放。但请注意,已知这些锁不会与当前存在的任何其他锁冲突。在从所有后端收集完快速路径锁的信息后,剩余的常规锁管理器作为一个整体被锁定,并作为原子操作收集所有剩余锁的一致快照。解锁常规锁管理器后,谓词锁管理器以类似的方式被锁定,并作为原子操作收集所有谓词锁。因此,除了快速路径锁之外,每个锁管理器都将提供一组一致的结果,但由于我们不会同时锁定两个锁管理器,因此在我们检查常规锁管理器之前以及在检查谓词锁管理器之前,可能会发生锁的获取或释放。

如果此视图被频繁访问,锁定常规锁管理器和/或谓词锁管理器可能会对数据库性能产生一定影响。锁只保持必要的最短时间以从锁管理器获取数据,但这并不能完全消除性能影响的可能性。

提交更正

如果您在文档中发现任何不正确之处、与特定功能的实际使用不符之处,或者需要进一步澄清之处,请使用 此表单 报告文档问题。