PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

53.13. pg_locks #

视图 pg_locks 提供对数据库服务器内活动进程 所持锁信息的访问。 关于加锁的更多讨论见 Chapter 13。

对每个活动的可加锁对象、请求的锁模式和相关进程的组合, pg_locks 各占一行。 因此,如果多个进程正在持有或等待对同一可加锁对象的锁, 该对象可能出现多次。 不过,当前没有任何锁的对象完全不会出现。

可加锁对象有几种不同的类型: 整个关系(例如表)、关系的单个页面、关系的单个元组、 事务 ID(虚拟 ID 和永久 ID 都是)、 以及一般的数据库对象 (以与 pg_description 或 pg_depend 相同的方式,由 class OID 和 object OID 标识)。 此外,扩展关系的权利被表示为一个单独的可加锁对象, 更新 pg_database.datfrozenxid 的权利也是。 另外,可以在具有用户定义含义的数字上获取 “咨询”锁。

Table 53.13. \1 字段列说明

Column Type

Description

locktype text

可加锁对象的类型: relation、 extend、 frozenid、 page、 tuple、 transactionid、 virtualxid、 spectoken、 object、 userlock、 advisory 或 applytransaction。 (另见 Table 27.11。)

database oid (references pg_database.oid)

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

relation oid (references pg_class.oid)

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

page int4

锁在该关系内针对的页面编号, 如果目标不是关系页面或元组则为空

tuple int2

锁在该页面内针对的元组编号, 如果目标不是元组则为空

virtualxid text

锁针对的事务的虚拟 ID, 如果目标不是虚拟事务 ID 则为空; 见 Chapter 67

transactionid xid

锁针对的事务的 ID,如果目标不是事务 ID 则为空; Chapter 67

classid oid (references pg_class.oid)

包含锁目标的系统目录的 OID,如果目标不是一般数据库对象则为 空

objid oid (references any OID column)

锁目标在其系统目录内的 OID,如果目标不是一般数据库对象则为 空

objsubid int2

锁针对的列编号 ( classid 和 objid 指该表本身), 如果目标是其他某种一般数据库对象则为零, 如果目标不是一般数据库对象则为空

virtualtransaction text

持有或等待这个锁的事务的虚拟 ID

pid int4

持有或等待这个锁的服务器进程的进程 ID, 如果该锁被一个预备好的事务持有则为空

mode text

该进程持有或期望的锁模式的名称 (见 Section 13.3.1 和 Section 13.2.3)

granted bool

如果锁已被持有则为 true,如果锁正在被等待则为 false

fastpath bool

如果锁是通过快速路径获取的则为 true,如果是通过主锁表获取的 则为 false

waitstart timestamptz

服务器进程开始等待这个锁的时间, 如果锁已被持有则为空。 注意,在等待开始后的极短时间内,即使 granted 为 false,这个值也可能是空的。


在代表被指定进程持有的锁的行中, granted 为 true。 false 表示该进程当前正在等待获取这个锁,这暗示至少有一个其他 进程正在持有或等待同一可加锁对象上的一个冲突锁模式。 等待中的进程会休眠,直到另一个锁被释放(或检测到死锁 情况)。 单个进程一次至多只能等待获取一个锁。

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

虽然元组是一种可加锁的对象类型, 但关于行级锁的信息存储在磁盘上而不是内存中, 因此行级锁通常不会出现在这个视图中。 如果一个进程正在等待一个行级锁, 它通常会以等待该行锁当前持有者的永久事务 ID 的形式出现在 视图中。

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

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

应用事务锁在并行模式下用于在逻辑复制中应用事务。 远程事务 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() 函数 (见 Table 9.71)来标识一个等待 进程被哪些进程阻塞。

pg_locks 视图显示来自常规锁管理器和谓词 锁管理器两个独立系统的数据;此外,常规锁管理器把它自己的锁细分 为常规锁和 快速路径锁。 这些数据不保证完全一致。 当查询该视图时, 关于快速路径锁 ( fastpath = true)的数据会逐个地从每个后端收集, 而不冻结整个锁管理器的状态,因此在收集信息期间锁可能被获取或 释放。 不过,注意这些锁已知不会与当前已存在的任何其他锁冲突。 在查询完所有后端的快速路径锁之后,常规锁管理器的其余部分会作为 一个单元被加锁,并且所有剩余锁的一致性快照会作为一个原子动作被 收集。 在解锁常规锁管理器之后,谓词锁管理器会以相似的方式被加锁, 所有谓词锁会作为一个原子动作被收集。 因此,除快速路径锁之外,每个锁管理器都会交付一个一致的结果集合; 但由于我们不会同时锁定两个锁管理器, 在我们查询常规锁管理器之后、查询谓词锁管理器之前,锁仍可能被 获取或释放。

如果非常频繁地访问这个视图,对常规和/或谓词锁管理器加锁可能会 对数据库性能产生一些影响。 这些锁只会被持有从锁管理器获取数据所需的最低限度时间, 但这并不能完全消除性能影响的可能性。