Структура менеджера буферов в PostgreSQL
Менеджер буферов состоит из трех слоев: буферной таблицы, дескриптера буферов, а также буферного пула (рисунок 87):
- Буферный пул: Массив, в котором хранятся страницы файлов данных. Каждый слот в массиве обозначается как buffer_ids.
- Дескриптер буфера: Массив дескрипторов буфера. Каждый дескриптор имеет соответствие со слотом буферного пула и содержит метаданные страниц, хранящихся в соответствующем слоте.
Обратите внимание на то, что термин "слой дескрипторов буфера" используется исключительно в этом документе.
- Буферная таблица: Хеш-таблица, в которой хранятся отношения между тегами буфера сохраненных страниц и buffer_id дескрипторов, содержащих метаданные сохраненных страниц.
Буферная таблица
Буферную таблицу можно логически разделить на три части: хэш-функцию, слоты хэш-бакетов и записи данных (рис. 88).
Встроенная хэш-функция сопоставляет теги буфера со слотами хэш-пула. Несмотря на то, что количество слотов хэш-букетов больше, чем количество слотов буферного пула, могут возникать определенные коллизии, для разрешения которых в буферной таблице используется метод Separate Chaining.
Запись данных состоит из двух значений: буферного тега страницы и идентификатора буфера дескриптора, в котором хранятся метаданные страницы. Например, запись данных 'Tag_A, id=1' означает, что дескриптор буфера с идентификатором буфера '1' хранит метаданные страницы, помеченной тегом Tag_A.
Примечание: Хэш-функция
Хэш-функция представляет собой составную функцию calc_bucket() и hash().
uint32 bucket_slot = calc_bucket(unsigned hash(BufferTag buffer_tag), uint32 bucket_size)
Основные операции (поиск, вставка и удаление записей данных) здесь не рассматриваются. Это достаточно распространенные операции, описанные в последующих разделах.
Дескриптор буферов
В версии 9.6 была улучшена структура дескрипторов буферов. Сначала я расскажу о дескрипторах буферов, реализованных в версии 9.5 и более ранних версиях, а затем объясню, чем дескрипторы буферов версии 9.6 и новее отличаются от предыдущих версий.
Слой дескрипторов буфера описан в следующем подразделе.
Версия 9.5 и более ранние версии
Структура дескриптора буфера в версиях 9.5 и более ранних содержит метаданные страницы, хранящейся в соответствующем слоте буферного пула. Структура дескриптора буфера определяется структурой BufferDesc.
BufferDesc:
/* * Flags for buffer descriptors
*
* Note: TAG_VALID essentially means that there is a buffer hashtable
* entry associated with the buffer's tag.
*/
#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_IO_ERROR (1 << 4) /* previous I/O failed */
#define BM_JUST_DIRTIED (1 << 5) /* dirtied since write started */
#define BM_PIN_COUNT_WAITER (1 << 6) /* have waiter for sole pin */
#define BM_CHECKPOINT_NEEDED (1 << 7) /* must write for checkpoint */
#define BM_PERMANENT (1 << 8) /* permanent relation (not unlogged) */
src/include/storage/buf_internals.h
typedef struct sbufdesc
{
BufferTag tag; /* ID of page contained in buffer */
BufFlags flags; /* see bit definitions above */
uint16 usage_count; /* usage counter for clock sweep code */
unsigned refcount; /* # of backends holding pins on buffer */
int wait_backend_pid; /* backend PID of pin-count waiter */
slock_t buf_hdr_lock; /* protects the above fields */
int buf_id; /* buffer's index number (from 0) */
int freeNext; /* link in freelist chain */
LWLockId io_in_progress_lock; /* to wait for I/O to complete */
LWLockId content_lock; /* to lock access to buffer contents */
} BufferDesc;
- тег содержит buffer_tag сохраненной страницы в соответствующем слоте буферного пула.)
- buf_id идентифицирует дескриптер. Он эквивалентен buffer_id соответствующего слота буферного пула.
Счетчик ссылок или refcount содержит количество процессов PostgreSQL, которые в данный момент обращаются к хранимой странице.
-
Его также называют pin count:
Когда процесс начинает работать с буфером, он увеличивает refcount на один. По окончании работы счетчик уменьшается на один. Если значение refcount больше нуля, говорят, что буфер закреплен, или pinned. Закрепленный буфер не может быть вытеснен. - Счетчик числа обращений или usage_count содержит информацию о количестве обращений к хранимой странице с момента ее загрузки в соответствующий слот буферного пула. Он используется в алгоритме замены страниц (подраздел 8.4.4)
- Блокировки content_lock и io_in_progress_lock – легкие блокировки, которые захватывают на короткое времятребуемое для работы со структурой данных (например, с хеш-таблицей или списком указателей). Как правило, легкая блокировка удерживается недолго, но в некоторых случаях они защищают операции ввода-вывода, так что в принципе время может оказаться и значительным.
- Аргумент flags может содержать несколько состояний связанной хранимой страницы, основными из которых являются следующие:
- Грязный бит (dirty bit) указывает на то, что хранимая страница является грязной;
- Valid бит указывает на то, является ли хранимая страница действительной, то есть может ли она быть прочитана или записана.
Если этот бит действителен, то в соответствующем слоте буферного пула хранится страница, а дескриптор содержит метаданные страницы, то есть сохраненная страница может быть прочитана или записана.
Если этот бит недействителен, то дескриптор не содержит метаданных, соответственно, сохраненная страница не может быть прочитана или записана.
- Бит io_in_progress указывает на то, читает ли или записывает менеджер буфера связанную страницу из/ в хранилище.
- buf_hdr_lock – cпинлок, защищающий поля flags, usage_count и refcount.
- freeNext указатель на следующий дескриптор для создания freelist.
Версия 9.6 и новее
Структура дескриптера буферов определяется структурой BufferDesc.
BufferDesc:
/*
* Flags for buffer descriptors
*
* Note: BM_TAG_VALID essentially means that there is a buffer hashtable
* entry associated with the buffer's tag.
*/
#define BM_LOCKED (1U << 22) /* buffer header is locked */
#define BM_DIRTY (1U << 23) /* data needs writing */
#define BM_VALID (1U << 24) /* data is valid */
#define BM_TAG_VALID (1U << 25) /* tag is assigned */
#define BM_IO_IN_PROGRESS (1U << 26) /* read or write in progress */
#define BM_IO_ERROR (1U << 27) /* previous I/O failed */
#define BM_JUST_DIRTIED (1U << 28) /* dirtied since write started */
#define BM_PIN_COUNT_WAITER (1U << 29) /* have waiter for sole pin */
#define BM_CHECKPOINT_NEEDED (1U << 30) /* must write for checkpoint */
#define BM_PERMANENT (1U << 31) /* permanent buffer (not unlogged,
* or init fork) */
#define PG_HAVE_ATOMIC_U32_SUPPORT
typedef struct pg_atomic_uint32
{
volatile uint32 value;
} pg_atomic_uint32;
typedef struct BufferDesc
{
BufferTag tag; /* ID of page contained in buffer */
int buf_id; /* buffer's index number (from 0) */
/* state of the tag, containing flags, refcount and usagecount */
pg_atomic_uint32 state;
int wait_backend_pgprocno; /* backend of pin-count waiter */
int freeNext; /* link in freelist chain */
LWLock content_lock; /* to lock access to buffer contents */
} BufferDesc;
- tag хранит тег буфера сохраненной страницы в соответствующем слоте буферного пула.
- buf_id идентифицирует дескриптер.
- content_lock - легкая блокировка, используемая для контроля доступа к связанной хранимой странице.
- freeNext - указатель на следующий дескриптор для создания freelist.
- states может содержать несколько состояний и переменных связанной хранимой страницы, таких как refcount и usage_count.
Поля flags, usage_count и refcount были объединены в 32-битные данные (состояния) для использования атомарных операций. Поэтому блокировка io_in_progress_lock и блокировка buf_hdr_lock были удалены, так как больше нет необходимости защищать эти значения.
Структура BufferDesc определяется src/include/storage/buf_internals.h
Примечание: Атомарные операции
Вместо исключительного контроля над данными со стороны программного обеспечения атомарные операции процессора используют аппаратный исключительный контроль, а также атомарные операции чтения-изменения-записи.
Состояния дескриптора
Выделяют три состояния дескриптора:
- Empty: Если соответствующий слот буферного пула не хранитстраница (т.е. refcount и usage_count равны 0), состояние этого дескриптора определяется как пустое (empty).
- Pinned: Когда в соответствующем слоте буферного пула хранится страница и все процессы PostgreSQL обращаются к ней (т.е. refcount и usage_count больше или равны 1), состояние этого буферного дескриптора становится закрепленным (pinned);
- Unpinned: Когда в соответствующем слоте буферного пула хранится страница, но ни один процесс PostgreSQL не обращается к ней (т.е. usage_count больше или равен 1, но refcount равен 0), состояние этого буферного дескриптора становится незакрепленным (unpinned).
Каждый дескриптор будет иметь одно из указанных выше состояний. Состояние дескриптора меняется в зависимости от условий, которые описаны в следующем подразделе.
На следующих рисунках состояния дескрипторов выделены следующими цветами:
- (белый цвет) - Empty
- (голубой цвет) - Pinned
- (цвет морской волны) - Unpinned
Грязная страница обозначается как 'X'. Например, unpinned грязный дескриптор представлен как X .
Уровень дескрипторов буфера
Дескрипторы буфера образует массив, который в данном документе называется «слоем дескрипторов буфера».
При запуске сервера PostgreSQL состояние всех дескрипторов буфера - "empty". В PostgreSQL эти дескрипторы представляют собой связный список, называемый freelist (рис. 89).
Примечание: Freelist
Важно отметить, что freelist в PostgreSQL не имеет никакого отношения к freelist в Oracle. Freelist в PostgreSQL - это просто связанный список пустых дескрипторов буфера. В рамках PostgreSQL задачи freelist Oracle выполняют карты свободного пространства (FSM), которые описаны в подразделе 5.3.4.
Рисунок 90 отображает процесс загрузки первой страницы:
- Получите пустой дескриптор из верхней части списка freelist и закрепите его (т.е. увеличьте его refcount и usage_count на 1).
- Вставьте новую запись в буферную таблицу которая сопоставляет тег первой страницы с buffer_id полученного дескриптора.
- Загрузите новую страницу из хранилища в соответствующий слот буферного пула..
- Сохраните метаданные новой страницы в полученном дескрипторе.
Вторая и последующие страницы загружаются таким же образом. Более подробную информацию Вы найдете в подразделе 8.4.
Дескрипторы, извлеченные из freelist, всегда содержат метаданные страницы. Другими словами, непустые дескрипторы не возвращаются во freelist после того, как они были использованы. Однако соответствующие дескрипторы снова добавляются во freelist, если происходит одно из следующих событий:
- Таблицы или индексы удалены.
- БД удалены.
- Таблицы или индексы очищены с помощью команды VACUUM FULL.
Примечание: Почему empty дескрипторы формируютт freelist?
Freelist создается для немедленного извлечения первого дескриптора. Это обычная Более подробная информация доступна по ссылке.
Уровень дескрипторов буфера содержит беззнаковую 32-битную целочисленную переменную nextVictimBuffer. Эта переменная используется в алгоритме замены страниц, описанном в подразделе 8.4.
Буферный пул
Буферный пул - это массив, в котором хранятся страницы файлов данных, такие как таблицы и индексы. Индексы массива буферного пула называются buffer_ids.
Буфер является 8-КБ страницей в памяти, то есть имеет тот же размер, что и одна страница данных.







