Skip to content

54.12.pg_locks#

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

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

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

表 54.12.pg_locks

列类型

描述

locktype text

可锁定对象的类型:relationextendfrozenidpagetupletransactionidvirtualxidspectokenobjectuserlockadvisoryapplytransaction。(另请参见 表 28.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;请参阅 第 74 章

transactionid xid

目标锁定事务的 ID,如果目标不是事务 ID,则为 null;第 74 章

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 和推测插入令牌组成。推测插入令牌显示在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()函数(请参见表 9.67)来识别等待进程被哪个进程阻塞。

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

如果非常频繁地访问此视图,则锁定普通锁管理器和/或谓词锁管理器可能会对数据库性能产生一些影响。锁仅在从锁管理器获取数据所需的最小时间内保持,但这并不能完全消除对性能产生影响的可能性。