Блокировки менеджера буферов PostgreSQL
Менеджер буферов использует множество блокировок для различных целей. В этом разделе описаны блокировки, которые необходимы для понимания последующих разделов.
Примечание: Блокировки
Обратите внимание на то, что блокировки, описанные в этом разделе, являются частью механизма синхронизации менеджера буферов. Они не имеют ничего общего с операторами SQL или опциями SQL.
Блокировки буферных таблиц
BufMappingLock защищает целостность данных всей буферной таблицы. Это легковесная блокировка, которая может использоваться как в общем, так и в эксклюзивном режимах. При поиске записи в буферной таблице backend процесс владеет общей блокировкой BufMappingLock. При вставке или удалении записей backend процесс использует эксклюзивную блокировку.
BufMappingLock состоит из разделов (по умолчанию их колисество составляет 128 разделов). Каждый раздел BufMappingLock отвечает за каждую часть соответствующего слота хэш-бакета.
На рис. 91 показан пример результата разделения BufMappingLock. Два backend процесса могут одновременно удерживать соответствующие разделы BufMappingLock в эксклюзивном режиме для вставки новых записей данных. Если бы BufMappingLock была единой общесистемной блокировкой, обоим процессам пришлось бы ждать завершения работы другого процесса, в зависимости от того, какой процесс запустился первым.
Буферной таблице требуется множество других блокировок. Например, для удаления записи в буферной таблице используется спин-блокировка. Описание других блокировок опускается, поскольку в рамках данного документа в них нет необходимости.
Примечание:
Вплоть до версии 9.4 BufMappingLock по умолчанию была подразделена на 16 разделов.
Блокировки для каждого декриптора буферов
В версиях 9.5 и более ранних версиях для контроля доступа к хранимой странице в соответствующем слоте буферного пула каждый дескриптор буфера использовал две легковесные блокировки, а именно content_lock и io_in_progress_lock, buf_hdr_lock) использовался в том случае, если проверялись или изменялись значения его собственных полей (т. е. usage_count, refcount, flags).
В версии 9.6 методы доступа к буферам были значительно улучшены. Блокировки io_in_progress_lock и buf_hdr_lock были удалены. Вместо этих блокировок используются атомарные операции.
content_lock
Блокировка content_lock - это типичная блокировка, обеспечивающая ограничения доступа. Она может быть использована как в общем, так и в эксклюзивном режиме.
При чтении страницы backend процесс получает блокировку content_lock буферного дескриптора, в котором хранится страница.
Эксклюзивная блокировка content_lock возможна при выполнении хотя бы одного из следующих действий:
- Добавление строк (кортежей) в хранимую страницу или изменение полей t_xmin/t_xmax в хранимой странице. (t_xmin и t_xmax описаны в подразделе 5.2; при удалении или обновлении строк эти поля связанных с ними кортежей изменяются).
- Физическое удаление кортежей или уплотнение свободного пространства на хранимой странице. (эти операции выполняется с помощью VACUUM и HOT, которые описаны в разделах 6 и 7 соответственно).
- Замораживание кортежей в пределах хранимой страницы. (данный процесс описан в подразделах 5.10.1 и 6.3.)
Более подробную информацию Вы найдете в README.
io_in_progress_lock (версия 9.5 и более ранние версии)
В версии 9.5 и более ранних версиях io_in_progress_lock использовалась для ожидания завершения ввода-вывода в буфере. Когда процесс PostgreSQL загружает или записывает данные страницы из/в хранилище, он получает эксклюзивную блокировку io_in_progress соответствующего дескриптора на время доступа к хранилищу.
spinlock (версия 9.5 и более ранние версии)
При проверке флагов или других полей (например, refcount и usage_count) используется spin. Ниже приведены два примера использования данной блокировки:
- Прикрепление дескриптора буфера:
- Применение spinlock к дикриптору буфера.
- Увеличение его refcount и usage_count на 1.
- Снятие блокировки
LockBufHdr(bufferdesc); /* Acquire a spinlock */ bufferdesc->refcont++; bufferdesc->usage_count++; UnlockBufHdr(bufferdesc); /* Release the spinlock */
- Установление грязного бита на ‘1’:
- Применение spinlock к дикриптору буфера.
- Установите грязного бита на '1' с помощью побитовой операции.
- Снятие блокировки
#define BM_DIRTY (1 << 0) /* data needs writing */ #define BM_VALID (1 << 1) /* data is valid */ #define BM_TAG_VALID (1 << 2) /* tag is assigned */ #define BM_IO_IN_PROGRESS (1 << 3) /* read or write in progress */ #define BM_JUST_DIRTIED (1 << 5) /* dirtied since write started */ LockBufHdr(bufferdesc); bufferdesc->flags |= BM_DIRTY; UnlockBufHdr(bufferdesc);
Изменение других битов происходит таким же образом.




