↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 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

52.12. pg_locks #

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

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

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

表 52.12. pg_locks 列

列类型

描述

locktype text

可锁定对象的类型:relation,extend,frozenid,page,tuple,transactionid,virtualxid,spectoken,object,userlock,advisory 或 applytransaction。(另请参见表 27.11。)

database oid(引用 pg_database.oid)

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

relation oid(引用 pg_class.oid)

作为锁目标的关系的 OID,如果目标既不是关系,也不是关系的一部分则此列为 NULL

page int4

作为锁目标的页在关系中的页号,如果目标不是一个关系页或元组则此列为 NULL

tuple int2

作为锁目标的元组在页中的元组号,如果目标不是一个元组则此列为 NULL

virtualxid text

作为锁目标的事务的虚拟 ID,如果目标不是虚拟事务 ID,则为 NULL;见第 66 章

transactionid xid

作为锁目标的事务的 ID,如果目标不是事务 ID,则为 NULL;第 66 章

classid oid(引用 pg_class.oid)

包含锁目标的系统目录的 OID,如果目标不是一个普通数据库对象则此列为 NULL

objid oid(引用任意 OID 列)

锁目标在它的系统目录中的 OID,如果目标不是一个普通数据库对象则为 NULL

objsubid int2

锁的目标列号(classid 和 objid 指表本身),如果目标是某种其他普通数据库对象则此列为 0,如果目标不是一个普通数据库对象则此列为 NULL

virtualtransaction text

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

pid int4

持有这个锁或者正在等待这个锁的服务器进程的 PID,如果此锁被一个预备事务所持有则此列为 NULL

mode text

此进程已持有或者希望持有的锁模式的名称(参见第 13.3.1 节和第 13.2.3 节)

granted bool

如果锁已授予则为真,如果锁被等待则为假

fastpath bool

如果锁通过快速路径获得则为真,通过主锁表获得则为假

waitstart timestamptz

服务器进程开始等待该锁的时间;如果锁已被授予则为 NULL。注意,在开始等待之后,即使 granted 为 false,该值在极短时间内也可能仍为 NULL。


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

在执行事务期间,服务器进程会对事务的虚拟事务 ID 持有独占锁。如果为事务分配了永久 ID(通常仅在事务改变数据库状态时才会发生),它还会对事务的永久事务 ID 持有独占锁直到事务结束。当一个进程发现有必要等待另一个事务结束时,它会尝试获取另一个事务 ID(根据情况是虚拟 ID 还是永久 ID)的共享锁。只有当另一个事务终止并释放其锁时,这才会成功。

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

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

咨询锁可以在由单个 bigint 值或两个整数值组成的键上获取。一个 bigint 键在 classid 列中显示其高位半部分,在 objid 列中显示其低位半部分,并且 objsubid 等于 1。可以使用表达式(classid::bigint << 32) | objid::bigint 重新组装原始 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() 函数(参见表 9.69)来识别等待进程被哪些进程阻塞。

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

如果这个视图被非常频繁地访问,锁定常规和(或)谓词锁管理器可能会对数据库性能产生一些影响。锁仅在获取来自锁管理器的数据所需的最短时间内保持,但这并不能完全消除性能影响的可能性。

报告文档问题

阅读 上游文档. 通过 PostgreSQL 文档反馈表单.