49.63. pg_locks
Представление pg_locks даёт доступ к информации о блокировках, удерживаемых открытыми транзакциями на сервере баз данных. Подробнее блокировки рассматриваются в Главе 13.
Представление pg_locks содержит одну строку для каждого активного блокируемого объекта, запрошенного режима блокировки и запрашивающей транзакции. Таким образом, один и тот же блокируемый объект может фигурировать в этом представлении неоднократно, если его блокируют или ожидают блокировки несколько транзакций. Однако объекты, свободные от блокировок, в этом представлении отсутствуют вовсе.
Существует несколько различных типов блокируемых объектов: отношения целиком (например, таблицы), отдельные страницы отношений, отдельные кортежи отношений, идентификаторы транзакций (виртуальные и постоянные) и произвольные объекты баз данных (идентифицируемые по OID класса и OID объекта, так же как в pg_description или pg_depend). Кроме того, в виде отдельного блокируемого объекта представлено право расширения отношения. Также могут быть установлены «рекомендательные» блокировки, не имеющие предопределённого значения.
Таблица 49.64. Столбцы pg_locks
| Имя | Тип | Ссылки | Описание |
|---|---|---|---|
locktype | text | Тип блокируемого объекта: relation (отношение), extend (расширение отношения), page (страница), tuple (кортеж), transactionid (идентификатор транзакции), virtualxid (виртуальный идентификатор), object (объект), userlock (пользовательская блокировка) или advisory (рекомендательная) | |
database | oid | | OID базы данных, к которой относится цель блокировки, ноль, если это разделяемый объект, либо NULL, если целью является идентификатор транзакции |
relation | oid | | OID отношения, являющегося целью блокировки, либо NULL, если цель блокировки — не отношение или часть отношения |
page | integer | Номер страницы в отношении, являющейся целью блокировки, либо NULL, если цель блокировки — не страница или кортеж отношения | |
tuple | smallint | Номер кортежа на странице, являющегося целью блокировки, либо NULL, если цель блокировки — не кортеж | |
virtualxid | text | Виртуальный идентификатор транзакции, являющийся целью блокировки, либо NULL, если цель блокировки — другой объект | |
transactionid | xid | Идентификатор транзакции, являющийся целью блокировки, либо NULL, если цель блокировки — другой объект | |
classid | oid | | OID системного каталога, содержащего цель блокировки, либо NULL, если цель блокировки — не обычный объект базы данных |
objid | oid | любой столбец OID | OID цели блокировки в соответствующем системном каталоге, либо NULL, если цель блокировки — не обычный объект базы данных |
objsubid | smallint | Номер столбца, являющегося целью блокировки (на саму таблицу указывают classid и objid), ноль, если это некоторый другой обычный объект базы данных, либо NULL, если цель не обычный объект | |
virtualtransaction | text | Виртуальный идентификатор транзакции, удерживающей или ожидающей блокировку | |
pid | integer | Идентификатор серверного процесса (PID, Process ID), удерживающего или ожидающего эту блокировку, либо NULL, если блокировка удерживается подготовленной транзакцией | |
mode | text | Название режима блокировки, которая удерживается или запрашивается этим процессом (см. Подраздел 13.3.1 и Подраздел 13.2.3) | |
granted | boolean | True, если блокировка получена, и false, если она ожидается | |
fastpath | boolean | True, если блокировка получена по короткому пути, и false, если она получена через основную таблицу блокировок |
Признак granted устанавливается в строке, представляющей блокировку, удерживаемую указанной транзакцией. Если он сброшен, эта транзакция ждёт блокировки, из чего следует, что некоторая другая транзакция удерживает блокировку того же объекта в конфликтующем режиме. Ожидающая транзакция будет приостановлена до освобождения другой блокировки (или выявления ситуации взаимоблокировки). Одна транзакция в один момент времени может ожидать получения максимум одной блокировки.
Каждая транзакция удерживает исключительную блокировку для собственного виртуального идентификатора транзакции на всём своём протяжении. Если транзакции назначается постоянный идентификатор (что обычно происходит, только если транзакция изменяет состояние базы данных), она также удерживает до своего завершения блокировку этого постоянного идентификатора. Когда одна транзакция находит необходимым ожидать именно какую-то другую транзакцию, она делает это, запрашивая разделяемую блокировку для идентификатора другой транзакции (виртуального или постоянного, в зависимости от ситуации). Этот запрос будет выполнен, только когда другая транзакция завершится и освободит свои блокировки.
Хотя кортежи тоже представляют собой блокируемый объект, информация о блокировках строк хранится на диске, а не в памяти, поэтому такие блокировки обычно не показываются в этом представлении. Если транзакция ожидает блокировки на уровне строки, она обычно видна в нём как ожидающая постоянного идентификатора транзакции текущего владельца этой блокировки.
Рекомендательные блокировки могут быть получены по ключам, состоящим из одного значения bigint или из двух значений integer. Старшая половина bigint выводится в столбце classid, а младшая половина в столбце objid, и objsubid равен 1. Исходное значение bigint может быть восстановлено выражением (classid::bigint << 32) | objid::bigint. Для ключей integer первая часть ключа находится в classid, а вторая часть в objid, и objsubid равна 2. Конкретное предназначение этих ключей определяет пользователь. Рекомендательные блокировки существуют в рамках базы данных, поэтому столбец database имеет значение для таких блокировок.
Представление pg_locks даёт общую информацию по всем блокировкам в кластере баз данных, а не только по тем, что относятся к текущей базе. Хотя соединив relation с pg_class.oid, можно получить заблокированные отношения, это будет работать корректно только для отношений в текущей базе данных (для тех, в блокировках которых столбец database содержит OID текущей базы данных или ноль).
Соединив столбец pid со столбцом pid представления pg_stat_activity, можно получить дополнительную информацию о сеансах, удерживающих блокировки, например так:
SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa
ON pl.pid = psa.pid; Также, если вы используете подготовленные транзакции, столбец virtualtransaction можно соединить со столбцом transaction представления pg_prepared_xacts для получения дополнительной информации о подготовленных транзакциях, удерживающих блокировки. (Подготовленная транзакция не может ожидать блокировок, но она может продолжать удерживать блокировки, полученные ей в процессе выполнения.) Например:
SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
ON pl.virtualtransaction = '-1/' || ppx.transaction;В представлении pg_locks показываются данные и из менеджера обычных блокировок, и из менеджера предикатных блокировок, которые являются отдельными механизмами; кроме того, менеджер обычных блокировок подразделяет свои блокировки на обычные и полученные быстрым путём. Абсолютная согласованность всех этих данных не гарантируется. При обращении к этому представлению данные блокировок по быстрому пути (с fastpath = true) собираются по очереди с каждого серверного процесса, без замораживания состояния всего менеджера блокировок, так что существует возможность, что в процессе сбора этой информации блокировки будут освобождены или получены. Заметьте, однако, что эти блокировки не должны конфликтовать с любыми другими актуальными блокировками. После того как от всех процессов получены блокировки по быстрому пути, менеджер обычных блокировок замораживается целиком и информация обо всех оставшихся блокировках собирается в атомарной операции. После размораживания этого менеджера, также замораживается менеджер предикатных блокировок, и информация об этих блокировках собирается атомарно. Таким образом, за исключением блокировок по быстрому пути, каждый менеджер блокировок выдаёт согласованный набор результатов, но так как мы не блокируем оба этих менеджера одновременно, блокировки могут быть получены или освобождены после того, как опрашивается менеджер обычных блокировок, и до того, как опрашивается менеджер предикатных блокировок.
Блокировка менеджера обычных или предикатных блокировок может отразиться на производительности базы данных, если обращаться к этому представлению часто. Эта блокировка удерживается не дольше, чем необходимо для получения данных от менеджеров, но это не исключает возможность снижения производительности.
49.63. pg_locks
The view pg_locks provides access to information about the locks held by open transactions within the database server. See Chapter 13 for more discussion of locking.
pg_locks contains one row per active lockable object, requested lock mode, and relevant transaction. Thus, the same lockable object might appear many times, if multiple transactions are holding or waiting for locks on it. However, an object that currently has no locks on it will not appear at all.
There are several distinct types of lockable objects: whole relations (e.g., tables), individual pages of relations, individual tuples of relations, transaction IDs (both virtual and permanent IDs), and general database objects (identified by class OID and object OID, in the same way as in pg_description or pg_depend). Also, the right to extend a relation is represented as a separate lockable object. Also, “advisory” locks can be taken on numbers that have user-defined meanings.
Table 49.64. pg_locks Columns
| Name | Type | References | Description |
|---|---|---|---|
locktype | text | Type of the lockable object: relation, extend, page, tuple, transactionid, virtualxid, object, userlock, or advisory | |
database | oid | | OID of the database in which the lock target exists, or zero if the target is a shared object, or null if the target is a transaction ID |
relation | oid | | OID of the relation targeted by the lock, or null if the target is not a relation or part of a relation |
page | integer | Page number targeted by the lock within the relation, or null if the target is not a relation page or tuple | |
tuple | smallint | Tuple number targeted by the lock within the page, or null if the target is not a tuple | |
virtualxid | text | Virtual ID of the transaction targeted by the lock, or null if the target is not a virtual transaction ID | |
transactionid | xid | ID of the transaction targeted by the lock, or null if the target is not a transaction ID | |
classid | oid | | OID of the system catalog containing the lock target, or null if the target is not a general database object |
objid | oid | any OID column | OID of the lock target within its system catalog, or null if the target is not a general database object |
objsubid | smallint | Column number targeted by the lock (the classid and objid refer to the table itself), or zero if the target is some other general database object, or null if the target is not a general database object | |
virtualtransaction | text | Virtual ID of the transaction that is holding or awaiting this lock | |
pid | integer | Process ID of the server process holding or awaiting this lock, or null if the lock is held by a prepared transaction | |
mode | text | Name of the lock mode held or desired by this process (see Section 13.3.1 and Section 13.2.3) | |
granted | boolean | True if lock is held, false if lock is awaited | |
fastpath | boolean | True if lock was taken via fast path, false if taken via main lock table |
granted is true in a row representing a lock held by the indicated transaction. False indicates that this transaction is currently waiting to acquire this lock, which implies that some other transaction is holding a conflicting lock mode on the same lockable object. The waiting transaction will sleep until the other lock is released (or a deadlock situation is detected). A single transaction can be waiting to acquire at most one lock at a time.
Every transaction holds an exclusive lock on its virtual transaction ID for its entire duration. If a permanent ID is assigned to the transaction (which normally happens only if the transaction changes the state of the database), it also holds an exclusive lock on its permanent transaction ID until it ends. When one transaction finds it necessary to wait specifically for another transaction, it does so by attempting to acquire share lock on the other transaction ID (either virtual or permanent ID depending on the situation). That will succeed only when the other transaction terminates and releases its locks.
Although tuples are a lockable type of object, information about row-level locks is stored on disk, not in memory, and therefore row-level locks normally do not appear in this view. If a transaction is waiting for a row-level lock, it will usually appear in the view as waiting for the permanent transaction ID of the current holder of that row lock.
Advisory locks can be acquired on keys consisting of either a single bigint value or two integer values. A bigint key is displayed with its high-order half in the classid column, its low-order half in the objid column, and objsubid equal to 1. The original bigint value can be reassembled with the expression (classid::bigint << 32) | objid::bigint. Integer keys are displayed with the first key in the classid column, the second key in the objid column, and objsubid equal to 2. The actual meaning of the keys is up to the user. Advisory locks are local to each database, so the database column is meaningful for an advisory lock.
pg_locks provides a global view of all locks in the database cluster, not only those relevant to the current database. Although its relation column can be joined against pg_class.oid to identify locked relations, this will only work correctly for relations in the current database (those for which the database column is either the current database's OID or zero).
The pid column can be joined to the pid column of the pg_stat_activity view to get more information on the session holding or waiting to hold each lock, for example
SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa
ON pl.pid = psa.pid;
Also, if you are using prepared transactions, the virtualtransaction column can be joined to the transaction column of the pg_prepared_xacts view to get more information on prepared transactions that hold locks. (A prepared transaction can never be waiting for a lock, but it continues to hold the locks it acquired while running.) For example:
SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
ON pl.virtualtransaction = '-1/' || ppx.transaction;
The pg_locks view displays data from both the regular lock manager and the predicate lock manager, which are separate systems; in addition, the regular lock manager subdivides its locks into regular and fast-path locks. This data is not guaranteed to be entirely consistent. When the view is queried, data on fast-path locks (with fastpath = true) is gathered from each backend one at a time, without freezing the state of the entire lock manager, so it is possible for locks to be taken or released while information is gathered. Note, however, that these locks are known not to conflict with any other lock currently in place. After all backends have been queried for fast-path locks, the remainder of the regular lock manager is locked as a unit, and a consistent snapshot of all remaining locks is collected as an atomic action. After unlocking the regular lock manager, the predicate lock manager is similarly locked and all predicate locks are collected as an atomic action. Thus, with the exception of fast-path locks, each lock manager will deliver a consistent set of results, but as we do not lock both lock managers simultaneously, it is possible for locks to be taken or released after we interrogate the regular lock manager and before we interrogate the predicate lock manager.
Locking the regular and/or predicate lock manager could have some impact on database performance if this view is very frequently accessed. The locks are held only for the minimum amount of time necessary to obtain data from the lock managers, but this does not completely eliminate the possibility of a performance impact.