47.59. pg_locks

pg_locks提供有关在数据库服务器中由打开的事务持有的锁的信息。 参阅第 13 章获取有关锁的更多的讨论。

pg_locks对每个活跃的可锁定对象、请求的锁模式、 以及相关的事务保存一行。因此,如果多个事务持有或者等待对同一个对象的锁, 那么同一个可锁定的对象可能出现多次。不过,一个目前没有锁在其上的对象将肯定不会出现。

有好几种不同的可锁定对象:一个关系(也就是一个表)、关系中独立页面、 关系中独立的行、一个事务 ID(虚拟和永久ID)、以及一般的数据库对象 (用类别 OID 和对象 OID 标识,表示方法和pg_descriptionpg_depend一样)还有,扩展一个关系的权限也是用一种独立的可锁定对象表示的。 另外,"advisory"锁可以用在有用户定义的含义的数据上。

表 47-60. pg_locks 字段

名字类型引用描述
locktypetext  可锁定对象的类型: relation, extend, page, tuple, transactionid, virtualxid, object, userlock, 或 advisory
databaseoidpg_database.oid 目标所在的数据库的 OID ,如果目标是共享对象,那么就是零, 如果目标是一个事务 ID ,就是 null 。
relationoidpg_class.oid 关系的 OID ,如果目标不是关系,也不是关系的一部分,则为 null
pageinteger  关系内部的页面编号,如果目标不是行页不是关系页,则为null
tuplesmallint  页面里面的行编号,如果目标不是行,则为 null
virtualxidtext  事务的虚拟 ID ,如果目标不是虚拟事务 ID ,就是 null
transactionidxid  事务的 ID ,如果目标不是事务 ID ,就是 null
classidoidpg_class.oid 包含该目标的系统表的 OID ,如果目标不是普通数据库对象,则为 null
objidoid任意OID属性 目标在其系统表内的 OID ,如果目标不是普通数据库对象,则为 null
objsubidsmallint  字段编号(classidobjid指向表自身)。 如果目标是其它普通数据库对象,这个字段是零。如果这个目标不是普通数据库对象,则为 null
virtualtransactiontext  持有此锁或者在等待此锁的事务的虚拟 ID
pidinteger  持有或者等待这个锁的服务器进程的进程 ID 。如果锁是被一个预备事务持有的,那么为 null
modetext 这个进程持有的或者是期望的锁模式(参阅第 13.3.1 节第 13.2.3 节)
grantedboolean 如果持有锁,为真,如果等待锁,为假
fastpathboolean 如果锁通过快速路径获得为真,如果通过主锁表获得为假

granted为真时表明指定事务持有一个锁。为假则表明该事务当前等待使用这个锁, 这就暗示着某个其它的事务正在同样的可锁定对象上持有冲突的锁模式。等待的会话将一直睡眠, 直到另外一个锁释放(或者侦测到一个死锁条件)。一个事务一次最多等待一个锁。

每个事务都在它持续的时间里在他自己的虚拟事务 ID 上持有一个排他锁。如果给事务赋予一个永久ID (通常只在事务改变数据库的状态时发生),那么它也在它的永久事务ID上持有一个排它锁,直到它结束。 如果一个事务认为它必须等待另外一个事务,它会以企图在另外一个事务 ID 上获取共享锁的方式实现之 (虚拟的或永久的ID取决于情景)。这个锁只有在另外一个事务终止并且释放它的锁的前提下才能成功。

尽管行是一种可以锁定的对象,但是有关行级别锁的信息是存储在磁盘上的,而不是在内存里, 因此,行级别的锁通常不会出现在这个视图里。如果一个事务在等待一个行级别的锁, 那么它通常会在这个视图里以等待当前持有该行锁的永久事务 ID 的方式出现。

建议锁可以在由单独一个bigint值或两个integer值组成的键上获得。 一个 bigint键的高/低位部分分别在classidobjid 字段中显示,并且objsubid等于 1 。原先的bigint值可以通过 表达式(classid::bigint << 32) | objid::bigint重新组装。 integer 组成的键前半部分在classid字段中显示、后半部分在objid 字段中显示,并且objsubid等于 2 。键的实际含义取决于用户的定义。 建议锁是针对单个数据库的,因此database字段对于建议锁就显得很有意义了。

pg_locks提供了一个数据库集群里的所有的锁的全局视图, 而不仅仅那些和当前数据库相关的。尽管它的relation 字段可以和pg_class.oid连接起来以标识被锁住的关系, 但是这个方法目前只能对在当前数据库里的关系有用(那些database 字段是当前数据库的 OID 或者零的数据库)。

pid字段可以可以和pg_stat_activity 视图的pid字段连接起来获取持有或者等待持有每个锁的会话的更多信息。 同样,如果你使用预备事务,可以把transaction字段和pg_prepared_xacts 视图的transaction字段连接起来获取持有锁的那个预备事务的更多信息。 一个预备事务不能等待任何锁,但是在运行的时候,它继续持有它已经请求到的锁。

pg_locks显示独立系统的普通锁管理器和谓词锁管理器的数据; 另外,普通锁管理器细分它的锁为普通和fast-path锁。这个数据不保证是完全一致的。 当请求视图时,fast-path锁的数据(fastpath = true) 从每个后端一次收集,在整个锁管器中没有冻结状态,所以当收集信息时,可以获取锁或释放锁。 不过,要注意的是,这些锁不和任意其他当前锁发生冲突。在所有后端已经查询fast-path锁之后, 剩余的普通锁管理器作为一个单元,并且一个所有剩余锁的一致的快照作为原子动作收集。 在解锁普通锁管理器之后,谓词锁管理器同样的锁住,并且所有谓词锁作为一个原子动作收集。 因此,除了fast-path锁,每个锁管理器将给出一致的结果集,但是因为我们不同时锁住锁管理器, 锁可能在我们访问普通锁管理器之后、访问谓词锁管理器之前获取或释放。

如果这个视图访问的非常频繁,那么锁住普通和/或谓词锁管理器可能会对数据库性能有些影响。 锁只持有从锁管理器获取数据所需要的最小的时间,但是这并不能完全消除性能影响的可能性。