# Блокировки разных объектов

Индекс LLMS: [llms.txt](/ru/llms.txt)

---

<span id="internal-ru-chapter-14" class="pdf-anchor"></span><span id="internal-ru-page-294" class="pdf-anchor"></span>

<span id="internal-ru-dummy-102" class="pdf-anchor"></span>

## 14.1. Блокировки неотношений {#internal-ru-section-14-1}

<span id="internal-ru-dummy-103" class="pdf-anchor"></span>

Когда требуется заблокировать ресурс, не являющийся *отношением* в понимании PostgreSQL, используются тяжелые блокировки типа `object`[^internal-ru-294-1]. Таким ресурсом может быть почти все, что можно найти в системном каталоге, например табличные пространства, подписки, схемы, роли, политики, перечислимые типы данных.

Начнем транзакцию и создадим в ней таблицу:

```text {label="psql"}
=> BEGIN;
=> CREATE TABLE example(n integer);
```

Теперь посмотрим на блокировки неотношений в `pg_locks`:

<span id="internal-ru-page-295" class="pdf-anchor"></span>

```text {label="psql"}
=> SELECT database,
  (
    SELECT datname FROM pg_database WHERE oid = database
  ) AS dbname,
  classid,
  (
    SELECT relname FROM pg_class WHERE oid = classid
  ) AS classname,
  objid,
  mode,
  granted
FROM pg_locks
WHERE locktype = 'object'
  AND pid = pg_backend_pid() \gx
-[ RECORD 1 ]--------------
database  | 16391
dbname    | internals
classid   | 2615
classname | pg_namespace
objid     | 2200
mode      | AccessShareLock
granted   | t
```

Блокируемый ресурс определяется здесь тремя полями:

**database** — `oid` базы данных, к которой относится блокируемый объект (или ноль, если это общий объект кластера);

**classid** — `oid` из `pg_class`, который соответствует имени таблицы системного каталога, определяющей тип ресурса;

**objid** — `oid` из таблицы системного каталога, на которую указал `classid`.

В столбце `database` указана база данных `internals`, к которой подключен сеанс. Столбец `classid` указывает на таблицу `pg_namespace`, содержащую схемы.

Теперь мы можем расшифровать `objid`:

```text {label="psql"}
=> SELECT nspname FROM pg_namespace WHERE oid = 2200;
 nspname
---------
 public
(1 row)
```

Таким образом, заблокирована схема `public`, чтобы никто не мог удалить ее, пока транзакция еще не завершена.

Соответственно, при удалении объектов захватываются исключительные блокировки как самого объекта, так и всех, от которых он зависит[^internal-ru-295-1].

```text {label="psql"}
=> ROLLBACK;
```

<span id="internal-ru-page-296" class="pdf-anchor"></span>

## 14.2. Блокировки расширения отношения {#internal-ru-section-14-2}

<span id="internal-ru-dummy-104" class="pdf-anchor"></span>

Когда число строк в отношении растет, PostgreSQL по возможности использует для вставки свободное место в имеющихся страницах. Но очевидно, что в какой-то момент приходится добавлять и новые, то есть *расширять отношение*. Физически страницы добавляются в конец соответствующего файла-сегмента (и это может привести к созданию нового сегмента).

Чтобы два процесса не начали добавлять страницы одновременно, эта операция защищена специальной тяжелой блокировкой с типом `extend`[^internal-ru-296-1]. Такая же блокировка используется и при очистке индексов, чтобы другие процессы не могли добавить новые страницы во время сканирования.

Блокировка расширения отношения ведет себя немного иначе, чем рассмотренные ранее:

- она снимается сразу по завершении расширения, не дожидаясь конца транзакции;

- она не может приводить к взаимоблокировкам, поэтому в процедуре обнаружения для нее сделано исключение: она не попадает в граф ожиданий.

> [!NOTE]
> Тем не менее процедура проверки все равно вызывается, если ожидание расширения происходит дольше, чем установлено параметром *deadlock\_timeout*. Это ненормальная ситуация, но она может возникнуть при большом потоке вставок из большого числа параллельно работающих процессов. В этом случае проверка может запускаться многократно, фактически парализуя нормальную работу системы.

При интенсивной вставке блокировка может стать узким местом, поэтому файлы таблиц расширяются не на одну страницу, а сразу на несколько (пропорционально числу ожидающих блокировку процессов, но не более чем на 64 страницы за один раз)[^internal-ru-296-2]. Однако к файлам индексов на основе B-дерева по-прежнему добавляется только по одной странице[^internal-ru-296-3]. (v. 16)

<span id="internal-ru-page-297" class="pdf-anchor"></span>

## 14.3. Блокировки страниц {#internal-ru-section-14-3}

<span id="internal-ru-dummy-105" class="pdf-anchor"></span>

Тяжелая блокировка на уровне страницы с типом `page` [^internal-ru-297-1] применяется в единственном случае, связанном с особенностями GIN-индексов. (*с.* [612](/ru/28-gin/#internal-ru-page-613))

GIN-индексы позволяют ускорять поиск элементов в составных значениях, например слов в текстовых документах. Такие индексы в первом приближении можно представить как обычное B-дерево, в котором хранятся не сами документы, а отдельные слова этих документов. Поэтому при добавлении нового документа индекс приходится перестраивать довольно сильно, внося в него каждое слово, входящее в документ.

Чтобы улучшить производительность, GIN-индексы предоставляют возможность отложенной вставки, которая включается параметром хранения *fastupdate*. Новые слова сначала быстро добавляются в неупорядоченный *список ожидания* (pending list), а спустя какое-то время все накопившееся перемещается в основную индексную структуру. Экономия происходит за счет того, что разные документы с большой вероятностью содержат повторяющиеся слова. (*по умолчанию:* `on`)

Чтобы исключить перемещение слов из списка ожидания в основной индекс одновременно несколькими процессами, на время переноса метастраница индекса блокируется в исключительном режиме. Это не мешает обычному использованию индекса.

Как и блокировка расширения отношения, блокировка страницы снимается сразу по завершении работы (а не в конце транзакции) и не может приводить к взаимоблокировкам.

## 14.4. Рекомендательные блокировки {#internal-ru-section-14-4}

<span id="internal-ru-dummy-106" class="pdf-anchor"></span><span id="internal-ru-page-298" class="pdf-anchor"></span>

В отличие от других тяжелых блокировок (таких как блокировки отношений), *рекомендательные блокировки* [^internal-ru-297-2] (advisory locks) никогда не устанавливаются автоматически, ими управляет разработчик приложения. Их удобно использовать, если приложению для каких-то целей требуется нестандартная логика блокирования.

Допустим, имеется ресурс, не соответствующий никакому объекту базы данных (который мы могли бы заблокировать командами типа `SELECT FOR` или `LOCK TABLE`). Для блокировки надо сопоставить ресурс с числовым идентификатором. Если у ресурса есть уникальное имя, то простой вариант — взять хеш-код от имени:

```text {label="psql"}
=> SELECT hashtext('ресурс1');
 hashtext
-----------
 243773337
(1 row)
```

Имеется целое семейство функций [^internal-ru-298-1] для управления рекомендательными блокировками. Их названия начинаются на `pg_advisory` и содержат дополнительные слова, уточняющие назначение функции:

**lock** — захватить блокировку;

**try** — захватить блокировку, если это можно сделать без ожидания;

**unlock** — освободить блокировку;

**share** — использовать разделяемый режим (по умолчанию используется исключительный режим);

**xact** — блокировать до конца транзакции (по умолчанию — до конца сеанса).

Для примера захватим исключительную блокировку до конца сеанса:

```text {label="psql"}
=> BEGIN;
=> SELECT pg_advisory_lock(hashtext('ресурс1'));
=> SELECT locktype, objid, mode, granted
FROM pg_locks WHERE locktype = 'advisory' AND pid = pg_backend_pid();
 locktype |   objid   |     mode      | granted
----------+-----------+---------------+---------
 advisory | 243773337 | ExclusiveLock | t
(1 row)
```

<span id="internal-ru-page-299" class="pdf-anchor"></span>

Чтобы блокирование действительно работало, другие процессы также должны придерживаться установленного порядка получения доступа к ресурсу. За соблюдением этого правила должно следить приложение.

Установленная блокировка не снимается и после завершения транзакции:

```text {label="psql"}
=> COMMIT;
=> SELECT locktype, objid, mode, granted
FROM pg_locks WHERE locktype = 'advisory' AND pid = pg_backend_pid();
 locktype |   objid   |     mode      | granted
----------+-----------+---------------+---------
 advisory | 243773337 | ExclusiveLock | t
(1 row)
```

После окончания работы с ресурсом освобождаем блокировку явно:

```text {label="psql"}
=> SELECT pg_advisory_unlock(hashtext('ресурс1'));
```

## 14.5. Предикатные блокировки {#internal-ru-section-14-5}

<span id="internal-ru-dummy-107" class="pdf-anchor"></span>

Термин *предикатная блокировка* появился еще при первых попытках реализовать полную изоляцию на основе блокировок[^internal-ru-299-1]. Проблема, с которой тогда столкнулись, заключалась в том, что даже блокировка всех прочитанных и измененных строк не дает полной изоляции: в таблице могут появиться *новые* строки, попадающие под прежние условия отбора, что приводит к возникновению *фантомов*. (*с.* [51](/ru/02-isolation/#internal-ru-page-52))

Поэтому было предложено блокировать не строки, а условия (предикаты). При выполнении запроса с предикатом *a* &gt; 10 блокировка самого предиката не даст добавить в таблицу новые строки, попадающие под это условие, и позволит избежать фантомов. Сложность в том, что при появлении запроса с другим условием, например *a* &lt; 20, требуется определить, пересекаются ли эти условия. В общем случае это алгоритмически неразрешимая задача; на практике ее можно решить только для предикатов, имеющих очень простой вид (как в приведенном примере).

<span id="internal-ru-page-300" class="pdf-anchor"></span>

В PostgreSQL уровень изоляции Serializable достигается иначе. Для него используется протокол *сериализуемой* изоляции на основе снимков (Serializable Snapshot Isolation, SSI) [^internal-ru-300-1]. Термин *предикатная блокировка* остался, но смысл его в корне изменился. Фактически такие «блокировки» ничего не блокируют, а используются для отслеживания зависимостей по данным между транзакциями.

Доказано, что изоляция на основе снимков, реализованная на уровне Repeatable Read, допускает *аномалию несогласованной записи* и *аномалию только* *читающей транзакции*, но никакие другие аномалии невозможны. Двум названным аномалиям соответствуют определенные закономерности в графе зависимостей между транзакциями, которые можно обнаруживать, не затрачивая на это слишком много ресурсов. (*с.* [67](/ru/02-isolation/#internal-ru-page-68))

Сложность в том, что необходимо различать зависимости двух видов:

- одна транзакция читает строку, которая затем изменяется другой транзакцией (RW-зависимость);

- одна транзакция изменяет строку, которую затем читает другая транзакция (WR-зависимость).

WR-зависимости можно обнаружить, используя уже имеющиеся обычные блокировки, а вот RW-зависимости приходится отслеживать дополнительно с помощью предикатных блокировок. Такое отслеживание включается при выборе уровня изоляции Serializable, и именно поэтому важно, чтобы *все* (или по крайней мере все взаимосвязанные) транзакции использовали этот уровень. Если какая-либо транзакция будет работать на другом уровне, она не будет устанавливать (и проверять) предикатные блокировки, и уровень Serializable выродится до Repeatable Read.

Еще раз повторюсь: несмотря на название, предикатные блокировки ничего не блокируют. Вместо этого при фиксации транзакции выполняется проверка, и если обнаруживается «нехорошая» структура зависимостей, которая может свидетельствовать об аномалии, транзакция обрывается.

<span id="internal-ru-page-301" class="pdf-anchor"></span>

Создадим таблицу с данными и индекс на ней, занимающий некоторое количество страниц (для этого указано низкое значение *fillfactor*):

```text {label="psql"}
=> CREATE TABLE pred(n numeric, s text);
=> INSERT INTO pred(n) SELECT n FROM generate_series(1,10000) n;
=> CREATE INDEX ON pred(n) WITH (fillfactor = 10);
=> ANALYZE pred;
```

Если запрос выполняется с помощью последовательного сканирования таблицы, предикатная блокировка устанавливается на всю таблицу целиком (даже если под условия фильтрации попадают не все строки).

```text {label="psql" hl_lines="11"}
=> SELECT pg_backend_pid();
 pg_backend_pid
----------------
          70273
(1 row)
=> BEGIN ISOLATION LEVEL SERIALIZABLE;
=> EXPLAIN (analyze, costs off, timing off, summary off, buffers off)
  SELECT * FROM pred WHERE n > 100;
                   QUERY PLAN
------------------------------------------------
 Seq Scan on pred (actual rows=9900.00 loops=1)
   Filter: (n > '100'::numeric)
   Rows Removed by Filter: 100
(3 rows)
```

Хотя предикатные блокировки используют собственную инфраструктуру, они отображаются в представлении `pg_locks` вместе с тяжелыми блокировками. Любые предикатные блокировки всегда захватываются в одном специальном режиме SIRead (Serializable Isolation Read):

<span id="internal-ru-page-302" class="pdf-anchor"></span>

```text {label="psql"}
=> SELECT relation::regclass, locktype, page, tuple
FROM pg_locks WHERE mode = 'SIReadLock' AND pid = 70273
ORDER BY 1, 2, 3, 4;
 relation | locktype | page | tuple
----------+----------+------+-------
 pred     | relation |      |
(1 row)
    => ROLLBACK;
```

<span id="internal-ru-dummy-108" class="pdf-anchor"></span>

Стоит иметь в виду, что предикатные блокировки не всегда снимаются сразу по завершении транзакции, ведь они нужны, чтобы отслеживать зависимости *между* транзакциями. Но в любом случае управление ими происходит автоматически.

Если же запрос выполняется с помощью индексного сканирования, ситуация меняется в лучшую сторону. В случае B-дерева достаточно установить предикатную блокировку на прочитанные табличные строки и на просмотренные листовые страницы индекса. Тем самым «блокируются» не только конкретные значения, но и весь прочитанный диапазон.

```text {label="psql" hl_lines="6"}
=> BEGIN ISOLATION LEVEL SERIALIZABLE;
=> EXPLAIN (analyze, costs off, timing off, summary off, buffers off)
  SELECT * FROM pred WHERE n BETWEEN 1000 AND 1001;
                            QUERY PLAN
-------------------------------------------------------------------
 Index Scan using pred_n_idx on pred (actual rows=2.00 loops=1)
   Index Cond: ((n >= '1000'::numeric) AND (n <= '1001'::numeric))
   Index Searches: 1
(3 rows)
```

```text {label="psql"}
=> SELECT relation::regclass, locktype, page, tuple
FROM pg_locks WHERE mode = 'SIReadLock' AND pid = 70273
ORDER BY 1, 2, 3, 4;
  relation  | locktype | page | tuple
------------+----------+------+-------
 pred       | tuple    |    4 |    96
 pred       | tuple    |    4 |    97
 pred_n_idx | page     |   28 |
(3 rows)
```

Число листовых страниц, покрывающих прочитанный диапазон, может измениться, например, из-за расщепления индексных страниц при вставке новых строк. Но реализация это учитывает и блокирует новые страницы:

<span id="internal-ru-page-303" class="pdf-anchor"></span>

```text {label="psql" hl_lines="11 12 13 14"}
=> INSERT INTO pred
  SELECT 1000+(n/1000.0) FROM generate_series(1,999) n;
=> SELECT relation::regclass, locktype, page, tuple
FROM pg_locks WHERE mode = 'SIReadLock' AND pid = 70273
ORDER BY 1, 2, 3, 4;
  relation  | locktype | page | tuple
------------+----------+------+-------
 pred       | tuple    |    4 |    96
 pred       | tuple    |    4 |    97
 pred_n_idx | page     |   28 |
 pred_n_idx | page     |  266 |
 pred_n_idx | page     |  267 |
 pred_n_idx | page     |  268 |
 pred_n_idx | page     |  269 |
(7 rows)
```

На каждую прочитанную версию строки создается отдельная блокировка, но потенциально таких версий может быть очень много. Для предикатных блокировок используется отдельный пул, память под который выделяется при старте сервера. Общее количество предикатных блокировок ограничено произведением значений параметров *max\_pred\_locks\_per\_transaction* и *max\_connections* (несмотря на названия параметров, учет по отдельным транзакциям не ведется). (*по умолчанию:* `64`) (*по умолчанию:* `100`)

По сути, возникает та же проблема, что и с блокировкой строк, но решается она по-другому: *повышением уровня* блокировок [^internal-ru-303-1].

Значения, при которых повышается уровень, можно задать конфигурационными параметрами. Если количество блокировок версий строк, относящихся к одной странице, превышает *max\_pred\_locks\_per\_page*, такие блокировки заменяются на одну блокировку уровня страницы. (*по умолчанию:* `2`)

```text {label="psql" hl_lines="5"}
=> EXPLAIN (analyze, costs off, timing off, summary off, buffers off)
  SELECT * FROM pred WHERE n BETWEEN 1000 AND 1002;
                            QUERY PLAN
-------------------------------------------------------------------
 Index Scan using pred_n_idx on pred (actual rows=3.00 loops=1)
   Index Cond: ((n >= '1000'::numeric) AND (n <= '1002'::numeric))
   Index Searches: 1
(3 rows)
```

<span id="internal-ru-page-304" class="pdf-anchor"></span>

Вместо трех блокировок типа `tuple` появилась одна типа `page`:

```text {label="psql" hl_lines="6"}
=> SELECT relation::regclass, locktype, page, tuple
FROM pg_locks WHERE mode = 'SIReadLock' AND pid = 70273
ORDER BY 1, 2, 3, 4;
  relation  | locktype | page | tuple
------------+----------+------+-------
 pred       | page     |    4 |
 pred_n_idx | page     |   28 |
 pred_n_idx | page     |  266 |
 pred_n_idx | page     |  267 |
 pred_n_idx | page     |  268 |
 pred_n_idx | page     |  269 |
(6 rows)
```

```text {label="psql"}
=> ROLLBACK;
```

По такому же принципу повышается уровень страничных блокировок. Если число блокировок страниц, относящихся к одному отношению, превышает *max\_pred\_locks\_per\_relation*, такие блокировки заменяются на одну блокировку уровня отношения. (При отрицательном значении в качестве предела берется значение *max\_pred\_locks\_per\_transaction*, деленное на модуль значения этого параметра; таким образом, по умолчанию предел составит 32). (*по умолчанию:* `-2`) (*по умолчанию:* `64`)

Конечно, повышение уровня блокировок неизбежно приводит к тому, что большее число транзакций напрасно завершается ошибкой сериализации. Пропускная способность системы в итоге снижается. Нужно искать баланс между расходом оперативной памяти на хранение блокировок и производительностью.

Предикатные блокировки поддерживают индексы следующих типов:

- B-деревья;

- хеш-индексы, GiST и GIN.

Если используется индексный доступ, а индекс не работает с предикатными блокировками, то блокировка накладывается на весь индекс целиком. Конечно, это тоже увеличивает число лишних обрывов транзакций.

<span id="internal-ru-page-305" class="pdf-anchor"></span>

Для более эффективной работы на уровне Serializable следует явно помечать только читающие транзакции как `READ ONLY`. Если менеджер блокировок убедится в невозможности конфликтов читающей транзакции с другими транзакциями[^internal-ru-305-1], он сможет освободить уже установленные для нее предикатные блокировки и не задействовать новые. А если дополнительно объявить такую транзакцию откладываемой (`DEFERRABLE`), это позволит избежать аномалии только читающей транзакции. (*с.* [71](/ru/02-isolation/#internal-ru-page-72))

[^internal-ru-294-1]: [backend/storage/lmgr/lmgr.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/lmgr.c;hb=REL_18_STABLE>), функции LockDatabaseObject и LockSharedObject.
[^internal-ru-295-1]: [backend/catalog/dependency.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/catalog/dependency.c;hb=REL_18_STABLE>), функция performDeletion.
[^internal-ru-296-1]: [backend/storage/lmgr/lmgr.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/lmgr.c;hb=REL_18_STABLE>), функция LockRelationForExtension.
[^internal-ru-296-2]: [backend/access/heap/hio.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/access/heap/hio.c;hb=REL_18_STABLE>), функция RelationAddBlocks.
[^internal-ru-296-3]: [backend/access/nbtree/nbtpage.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/access/nbtree/nbtpage.c;hb=REL_18_STABLE>), функция \_bt\_allocbuf.
[^internal-ru-297-1]: [backend/storage/lmgr/lmgr.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/lmgr.c;hb=REL_18_STABLE>), функция LockPage.
[^internal-ru-297-2]: [postgrespro.ru/docs/postgresql/18/explicit-locking#ADVISORY-LOCKS](<https://postgrespro.ru/docs/postgresql/18/explicit-locking#ADVISORY-LOCKS>).
[^internal-ru-298-1]: [postgrespro.ru/docs/postgresql/18/functions-admin#FUNCTIONS-ADVISORY-LOCKS](<https://postgrespro.ru/docs/postgresql/18/functions-admin#FUNCTIONS-ADVISORY-LOCKS>).
[^internal-ru-299-1]: [K.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [P.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [Eswaran,](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [J.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [N.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [Gray,](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [R.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [A.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [Lorie,](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [I.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [L.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [Traiger](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>)[.](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [The](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [notions](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [of](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [consistency](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [and](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [predicate](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [locks](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [in](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [a](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [database](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>) [system](<https://jimgray.azurewebsites.net/papers/on%20the%20notions%20of%20consistency%20and%20predicate%20locks%20in%20a%20database%20system%20cacm.pdf>).
[^internal-ru-300-1]: [backend/storage/lmgr/README-SSI](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/README-SSI;hb=REL_18_STABLE>); [backend/storage/lmgr/predicate.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/predicate.c;hb=REL_18_STABLE>).
[^internal-ru-303-1]: [backend/storage/lmgr/predicate.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/predicate.c;hb=REL_18_STABLE>), функция PredicateLockAcquire.
[^internal-ru-305-1]: [backend/storage/lmgr/predicate.c](<https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/storage/lmgr/predicate.c;hb=REL_18_STABLE>), макрос SxactIsROSafe.
