13.3. Явная блокировка#
13.3. Явная блокировка #
Tantor SE-1C предоставляет различные режимы блокировки для контроля параллельного доступа к данным в таблицах. Эти режимы могут использоваться для блокировки, контролируемой приложением, в ситуациях, когда MVCC не дает желаемого поведения. Кроме того, большинство команд Tantor SE-1C автоматически получают блокировки соответствующих режимов, чтобы гарантировать, что целевые таблицы не будут удалены или изменены несовместимым образом во время выполнения команды. (Например, TRUNCATE
не может быть безопасно выполнена одновременно с другими операциями на той же таблице, поэтому она получает блокировку ACCESS EXCLUSIVE
на таблицу для обеспечения этого).
Для изучения списка текущих ожидающих блокировок в сервере базы данных используйте системное представление pg_locks
. Дополнительную информацию о мониторинге состояния подсистемы управления блокировками см. в разделе Глава 26.
13.3.1. Таблица-уровень блокировок #
Список ниже показывает доступные режимы блокировки и контексты, в которых они автоматически используются Tantor SE-1C. Также можно явно получить любую из этих блокировок с помощью команды LOCK. Помните, что все эти режимы блокировки являются блокировками на уровне таблицы, даже если в названии есть слово “row”; названия режимов блокировки исторические. В некоторой степени названия отражают типичное использование каждого режима блокировки, но семантика одинакова. Единственное реальное отличие между режимами блокировки - это набор режимов блокировки, с которыми каждый конфликтует (см. Таблица 13.2). Две транзакции не могут удерживать блокировки конфликтующих режимов на одной и той же таблице одновременно. (Однако транзакция никогда не конфликтует сама с собой. Например, она может получить блокировку ACCESS EXCLUSIVE
и позже получить блокировку ACCESS SHARE
на той же таблице). Неконфликтующие режимы блокировки могут быть удерживаемыми одновременно множеством транзакций. Обратите внимание, что некоторые режимы блокировки конфликтуют сами с собой (например, блокировка ACCESS EXCLUSIVE
не может быть удерживаемой более чем одной транзакцией одновременно), в то время как другие не конфликтуют сами с собой (например, блокировка ACCESS SHARE
может быть удерживаемой несколькими транзакциями).
Режимы блокировки на уровне таблицы
-
ACCESS SHARE
(AccessShareLock
) Конфликты с режимом блокировки
ACCESS EXCLUSIVE
только.Команда
SELECT
получает блокировку этого режима на целевых таблицах. Обычно, любой запрос, который только читает таблицу и не изменяет ее, получит этот режим блокировки.-
ROW SHARE
(RowShareLock
) Конфликтует с режимами блокировки
EXCLUSIVE
иACCESS EXCLUSIVE
.Команда
SELECT
получает блокировку этого режима на всех таблицах, для которых указано одно из следующихFOR UPDATE
,FOR NO KEY UPDATE
,FOR SHARE
илиFOR KEY SHARE
(в дополнение к блокировкамACCESS SHARE
на любых других таблицах, на которые есть ссылки без явного указания опции блокировкиFOR ...
).-
ROW EXCLUSIVE
(RowExclusiveLock
) Конфликтует с режимами блокировки
SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
.Команды
UPDATE
,DELETE
,INSERT
иMERGE
получают этот режим блокировки на целевой таблице (в дополнение к блокировкамACCESS SHARE
на любые другие целевые таблицы). Обычно этот режим блокировки будет получен любой командой, которая изменяет данные в таблице.-
SHARE UPDATE EXCLUSIVE
(ShareUpdateExclusiveLock
) Конфликтует с режимами блокировки
SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
. Этот режим защищает таблицу от одновременных изменений схемы и запусковVACUUM
.Приобретено с помощью
VACUUM
(безFULL
),ANALYZE
,CREATE INDEX CONCURRENTLY
,CREATE STATISTICS
,COMMENT ON
,REINDEX CONCURRENTLY
, и определенные вариантыALTER INDEX
иALTER TABLE
(для полной информации см. документацию по этим командам).-
SHARE
(ShareLock
) Конфликты с режимами блокировки
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
. Этот режим защищает таблицу от одновременных изменений данных.Приобретено с помощью
CREATE INDEX
(безCONCURRENTLY
).-
SHARE ROW EXCLUSIVE
(ShareRowExclusiveLock
) Конфликты с режимами блокировки
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
. Этот режим защищает таблицу от одновременных изменений данных и является самоисключающим, поэтому только одна сессия может его удерживать в данный момент.Взято с помощью команды
CREATE TRIGGER
и некоторых формALTER TABLE
.-
EXCLUSIVE
(ExclusiveLock
) Конфликты с режимами блокировки
ROW SHARE
,ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
. Этот режим позволяет только одновременные блокировкиACCESS SHARE
, то есть только чтение из таблицы может выполняться параллельно с транзакцией, удерживающей этот режим блокировки.Приобретено с помощью
REFRESH MATERIALIZED VIEW CONCURRENTLY
.-
ACCESS EXCLUSIVE
(AccessExclusiveLock
) Соответствует блокировкам всех режимов (
ACCESS SHARE
,ROW SHARE
,ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
иACCESS EXCLUSIVE
). Этот режим гарантирует, что держатель является единственной транзакцией, которая имеет доступ к таблице любым способом.Приобретается с помощью команд
DROP TABLE
,TRUNCATE
,REINDEX
,CLUSTER
,VACUUM FULL
, иREFRESH MATERIALIZED VIEW
(безCONCURRENTLY
) . Многие формы командALTER INDEX
иALTER TABLE
также приобретают блокировку на этом уровне. Это также является режимом блокировки по умолчанию для операторовLOCK TABLE
, которые не указывают явно режим.
Подсказка
Только блокировка ACCESS EXCLUSIVE
препятствует выполнению
оператора SELECT
(без FOR UPDATE/SHARE
).
После получения блокировки она обычно удерживается до конца транзакции. Но если блокировка получена после установки точки сохранения, то блокировка освобождается немедленно, если точка сохранения откатывается. Это соответствует принципу, что ROLLBACK
отменяет все эффекты команд, выполненных после точки сохранения. То же самое относится к блокировкам, полученным внутри блока исключения PL/pgSQL: при выходе из блока из-за ошибки освобождаются блокировки, полученные внутри него.
Таблица 13.2. Конфликтующие режимы блокировки
Requested Lock Mode | Existing Lock Mode | |||||||
---|---|---|---|---|---|---|---|---|
ACCESS SHARE | ROW SHARE | ROW EXCL. | SHARE UPDATE EXCL. | SHARE | SHARE ROW EXCL. | EXCL. | ACCESS EXCL. | |
ACCESS SHARE | X | |||||||
ROW SHARE | X | X | ||||||
ROW EXCL. | X | X | X | X | ||||
SHARE UPDATE EXCL. | X | X | X | X | X | |||
SHARE | X | X | X | X | X | |||
SHARE ROW EXCL. | X | X | X | X | X | X | ||
EXCL. | X | X | X | X | X | X | X | |
ИСКЛЮЧИТ. ДОСТУП | X | X | X | X | X | X | X | X |
13.3.2. Блокировки на уровне строк #
В дополнение к блокировкам на уровне таблицы, существуют блокировки на уровне строк, которые автоматически используются Tantor SE-1C в следующих контекстах. См. Таблица 13.3 для полной таблицы конфликтов блокировок на уровне строк. Обратите внимание, что транзакция может удерживать конфликтующие блокировки на одной и той же строке, даже в разных подтранзакциях; но помимо этого, две транзакции никогда не могут удерживать конфликтующие блокировки на одной и той же строке. Блокировки на уровне строк не влияют на запросы данных; они блокируют только писателей и блокировщиков для одной и той же строки. Блокировки на уровне строк освобождаются при завершении транзакции или во время отката точки сохранения, так же как и блокировки на уровне таблицы.
Режимы блокировки на уровне строк
-
FOR UPDATE
FOR UPDATE
вызывает блокировку строк, полученных операторомSELECT
, как при выполнении операции обновления. Это предотвращает их блокировку, изменение или удаление другими транзакциями до завершения текущей транзакции. То есть, другие транзакции, которые пытаются выполнить операцииUPDATE
,DELETE
,SELECT FOR UPDATE
,SELECT FOR NO KEY UPDATE
,SELECT FOR SHARE
илиSELECT FOR KEY SHARE
для этих строк будут заблокированы до завершения текущей транзакции; соответственно,SELECT FOR UPDATE
будет ожидать одновременную транзакцию, которая выполнила любую из этих команд для той же строки, а затем заблокирует и вернет обновленную строку (или не вернет строку, если она была удалена). Однако, в рамках транзакции с уровнемREPEATABLE READ
илиSERIALIZABLE
будет сгенерирована ошибка, если строка, которую необходимо заблокировать, изменилась с момента начала транзакции. Для дальнейшего обсуждения см. Раздел 13.4.Режим блокировки
FOR UPDATE
также устанавливается при выполнении командыDELETE
для строки, а также при выполнении командыUPDATE
, которая изменяет значения определенных столбцов. В настоящее время для командыUPDATE
рассматриваются только те столбцы, которые имеют уникальный индекс, который может быть использован во внешнем ключе (при этом не рассматриваются частичные индексы и индексы на выражениях), но это может измениться в будущем.-
FOR NO KEY UPDATE
Поведение аналогично
FOR UPDATE
, за исключением того, что получаемая блокировка является слабее: эта блокировка не будет блокировать командыSELECT FOR KEY SHARE
, которые пытаются получить блокировку на те же строки. Этот режим блокировки также получается любымUPDATE
, который не получает блокировкуFOR UPDATE
.-
FOR SHARE
Ведет себя аналогично
FOR NO KEY UPDATE
, за исключением того, что он получает общую блокировку, а не исключительную блокировку для каждой извлеченной строки. Общая блокировка блокирует другие транзакции от выполненияUPDATE
,DELETE
,SELECT FOR UPDATE
илиSELECT FOR NO KEY UPDATE
для этих строк, но не мешает им выполнятьSELECT FOR SHARE
илиSELECT FOR KEY SHARE
.-
FOR KEY SHARE
Ведет себя аналогично
FOR SHARE
, за исключением того, что блокировка слабее:SELECT FOR UPDATE
блокируется, но неSELECT FOR NO KEY UPDATE
. Ключевая общая блокировка блокирует другие транзакции от выполненияDELETE
или любогоUPDATE
, который изменяет значения ключей, но не другиеUPDATE
, и также не предотвращаетSELECT FOR NO KEY UPDATE
,SELECT FOR SHARE
илиSELECT FOR KEY SHARE
.
Tantor SE-1C не сохраняет никакую информацию о измененных строках в памяти, поэтому нет ограничений на количество заблокированных строк одновременно. Однако блокировка строки может вызвать запись на диск, например, SELECT FOR UPDATE
изменяет выбранные строки для их пометки как заблокированные, и поэтому приводит к записи на диск.
Таблица 13.3. Конфликтующие блокировки на уровне строк
Requested Lock Mode | Current Lock Mode | |||
---|---|---|---|---|
ДЛЯ КЛЮЧА СО СОВМЕСТНЫМ ДОСТУПОМ | ДЛЯ СОВМЕСТНОГО ДОСТУПА | ДЛЯ ОБНОВЛЕНИЯ БЕЗ БЛОКИРОВКИ КЛЮЧА | ДЛЯ ОБНОВЛЕНИЯ | |
ДЛЯ КЛЮЧА СО СТАТУСОМ "SHARE" | X | |||
ДЛЯ ЧТЕНИЯ | X | X | ||
ДЛЯ ОБНОВЛЕНИЯ БЕЗ КЛЮЧА | X | X | X | |
ДЛЯ ОБНОВЛЕНИЯ | X | X | X | X |
13.3.3. Блокировки на уровне страницы #
В дополнение к блокировкам таблицы и строки, на уровне страниц используются общие/исключительные блокировки для контроля доступа на чтение/запись к страницам таблицы в общем буфере. Эти блокировки снимаются немедленно после извлечения или обновления строки. Разработчикам приложений обычно не нужно беспокоиться о блокировках на уровне страниц, но они упоминаются здесь для полноты.
13.3.4. Взаимные блокировки #
Использование явной блокировки может увеличить вероятность возникновения взаимоблокировок, когда две (или более) транзакции удерживают блокировки, которые другая транзакция хочет получить. Например, если транзакция 1 получает исключающую блокировку на таблицу A, а затем пытается получить исключающую блокировку на таблицу B, в то время как транзакция 2 уже удерживает исключающую блокировку на таблицу B и теперь хочет получить исключающую блокировку на таблицу A, то ни одна из них не может продолжить выполнение. Tantor SE-1C автоматически обнаруживает ситуации взаимоблокировки и разрешает их, прерывая одну из вовлеченных транзакций и позволяя другим завершиться. (Точно определить, какая транзакция будет прервана, сложно предсказать и на это нельзя полагаться).
Обратите внимание, что дедлоки могут возникать также в результате блокировки на уровне строк (и, следовательно, они могут возникать даже если явная блокировка не используется). Рассмотрим случай, когда две параллельные транзакции изменяют таблицу. Первая транзакция выполняет:
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;
Включается блокировка на уровне строки для строки с указанным номером счета. Затем вторая транзакция выполняется:
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222; UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;
Первый оператор UPDATE
успешно получает блокировку на уровне строки для указанной строки, поэтому он успешно обновляет эту строку. Однако второй оператор UPDATE
обнаруживает, что строка, которую он пытается обновить, уже заблокирована, поэтому он ожидает завершения транзакции, которая получила блокировку. Транзакция два теперь ожидает завершения транзакции один, прежде чем продолжить выполнение. Теперь транзакция один выполняется:
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;
Транзакция один пытается получить блокировку на уровне строки для указанной строки, но не может: транзакция два уже удерживает такую блокировку. Поэтому она ожидает завершения транзакции два. Таким образом, транзакция один блокируется на транзакции два, а транзакция два блокируется на транзакции один: возникает ситуация взаимной блокировки. Tantor SE-1C обнаружит эту ситуацию и прервет одну из транзакций.
Лучшая защита от дедлоков, как правило, заключается в их предотвращении путем обеспечения того, чтобы все приложения, использующие базу данных, получали блокировки на нескольких объектах в одном и том же порядке. В приведенном выше примере, если бы обе транзакции обновляли строки в одном и том же порядке, дедлок не произошел бы. Также следует обеспечить, чтобы первая блокировка, полученная на объекте в транзакции, была наиболее ограничивающим режимом, который будет необходим для этого объекта. Если невозможно проверить это заранее, то дедлоки могут быть обработаны на лету путем повторного выполнения транзакций, которые прерываются из-за дедлоков.
Пока не будет обнаружена ситуация блокировки, транзакция, запрашивающая блокировку на уровне таблицы или строк, будет бесконечно ждать, пока конфликтующие блокировки не будут освобождены. Это означает, что длительное открытие транзакций (например, во время ожидания пользовательского ввода) является плохой идеей для приложений.
13.3.5. Рекомендательные блокировки #
Tantor SE-1C предоставляет средство для создания блокировок, которые имеют определенное приложением значение. Они называются рекомендательными блокировками, потому что система не обязывает их использовать - это зависит от приложения, как их использовать правильно. Рекомендательные блокировки могут быть полезны для стратегий блокировки, которые неудобно сочетаются с моделью MVCC. Например, обычное использование рекомендательных блокировок - эмуляция пессимистических стратегий блокировки, типичных для так называемых систем управления данными “плоского файла”. Хотя флаг, хранящийся в таблице, может использоваться для той же цели, рекомендательные блокировки работают быстрее, избегают раздутия таблицы и автоматически очищаются сервером в конце сессии.
Есть два способа получить рекомендательную блокировку в Tantor SE-1C: на уровне сессии или на уровне транзакции. Полученная на уровне сессии рекомендательная блокировка сохраняется до явного освобождения или завершения сессии. В отличие от стандартных запросов на блокировку, запросы на рекомендательную блокировку на уровне сессии не учитывают семантику транзакции: блокировка, полученная во время транзакции, которая позже откатывается, все равно будет сохранена после отката, и, аналогично, разблокировка будет действительна даже если вызывающая транзакция завершится с ошибкой. Блокировка может быть получена несколько раз владеющим процессом; для каждого завершенного запроса на блокировку должен быть соответствующий запрос на разблокировку, прежде чем блокировка будет фактически освобождена. Запросы на блокировку на уровне транзакции, с другой стороны, ведут себя более похоже на обычные запросы на блокировку: они автоматически освобождаются в конце транзакции, и нет явной операции разблокировки. Такое поведение часто более удобно, чем поведение на уровне сессии, для краткосрочного использования рекомендательной блокировки. Запросы на блокировку на уровне сессии и на уровне транзакции для одного и того же идентификатора рекомендательной блокировки будут блокировать друг друга ожидаемым образом. Если сессия уже удерживает определенную рекомендательную блокировку, дополнительные запросы от нее всегда будут успешными, даже если другие сессии ожидают блокировки; это утверждение верно независимо от того, является ли существующая блокировка на уровне сессии или на уровне транзакции.
Как и все блокировки в Tantor SE-1C, полный список рекомендательных блокировок, в настоящее время удерживаемых любой сессией, можно найти в системном представлении pg_locks
.
Все рекомендательные блокировки и обычные блокировки хранятся в общем пуле общей памяти, размер которого определяется переменными конфигурации max_locks_per_transaction и max_connections. Необходимо быть осторожным, чтобы не исчерпать эту память, иначе сервер не сможет предоставить никаких блокировок. Это накладывает верхний предел на количество рекомендательных блокировок, которые может предоставить сервер, обычно в диапазоне от десятков до сотен тысяч, в зависимости от конфигурации сервера.
В некоторых случаях при использовании методов рекомендательной блокировки, особенно в запросах, включающих явное упорядочивание и ограничения LIMIT
, необходимо контролировать получаемые блокировки из-за порядка вычисления SQL-выражений. Например:
SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- danger! SELECT pg_advisory_lock(q.id) FROM ( SELECT id FROM foo WHERE id > 12345 LIMIT 100 ) q; -- ok
В приведенных выше запросах вторая форма опасна, потому что LIMIT
не гарантируется применяться перед выполнением функции блокировки. Это может привести к получению некоторых блокировок, которые приложение не ожидало, и, следовательно, не смогло бы их освободить (пока не завершится сессия). С точки зрения приложения такие блокировки будут висячими, хотя они все еще будут видны в pg_locks
.
Все функции, предоставляемые для работы с рекомендательными блокировками, описаны в Раздел 9.27.10.