Журналирование
Буферный кеш
13
Авторские права
© Postgres Professional, 2016–2022.
Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Устройство и использование буферного кеша
Механизм вытеснения страниц
Массовое вытеснение и буферные кольца
Настройка размера кеша
Локальный кеш для временных таблиц
Прогрев кеша
3
Буферный кеш
2 1 0
свободные
буферы
shared_buffers
грязный
буфер
хеш-таблица
1
число
обращений
следующая
жертва
Задача буферного кеша — сглаживать разницу в производительности
двух типов памяти: оперативной (быстрая, но мало) и дисковой
(медленная, но много). Чтобы работать с данными — читать или
изменять, — процессы читают страницы в буферный кеш. Пока
страница находится в кеше, мы экономим на обращениях к диску.
Буферный кеш располагается в общей памяти сервера и представляет
собой массив буферов. Каждый буфер состоит из места под одну
страницу данных и заголовка. Заголовок содержит, в числе прочего:
- расположение страницы на диске (файл и номер страницы в нем);
- число обращений к буферу (счетчик увеличивается каждый раз, когда
процесс читает или изменяет буфер);
- признак того, что данные на странице изменились и рано или поздно
должны быть записаны на диск (такой буфер называют грязным);
Размер буферного кеша задается параметром shared_buffers. Его
изменение требует перезапуска сервера.
Изначально кеш содержит пустые буферы, и все они связаны в список
свободных буферов. Смысл указателя на «следующую жертву» станет
ясен чуть позже.
Чтобы быстро находить нужную страницу в кеше, используется хеш-
таблица.
4
Страница в кеше
свободные
буферы
хеш-таблица
13
+1
буфер
закреплен
следующая
жертва
1 0
Когда процессу требуется прочитать страницу, он сначала пытается
найти ее в буферном кеше с помощью хеш-таблицы.
Ключом хеширования служит файл и номер страницы внутри файла;
получив номер буфера, процесс быстро проверяет, действительно ли
он содержат нужную страницу. Как и в любой хеш-таблице, здесь
возможны коллизии; в таком случае процессу придется проверить
несколько страниц.
Если нужная страница найдена в кеше, процесс должен «закрепить»
буфер (увеличить счетчик pin count) и увеличить число обращений
(счетчик usage count). Буфер могут закреплять несколько процессов
одновременно, поэтому используется именно счетчик, а не логический
признак.
Пока буфер закреплен (значение pin count больше нуля), считается, что
буфер используется и его содержимое не должно «радикально»
измениться. Например, в странице может появиться новая версия
строки — это никому не мешает благодаря многоверсионности и
правилам видимости. Но в закрепленный буфер не может быть
прочитана другая страница.
6
Чтение в свободный буфер
свободные
буферы
хеш-таблица
13 1
следующая
жертва
1 0
Может получиться так, что необходимая страница не будет найдена
в[кеше. В этом случае ее необходимо считать с диска в какой-либо
буфер.
В первую очередь поиск подходящего буфера будет проходить по
списку свободных буферов, если он не пуст.
Найденный по указателю буфер закрепляется, в него читается
необходимая страница, число обращений устанавливается в единицу.
Указатель на свободный буфер передвигается на следующий по списку
буфер, если он есть.
Кроме того, ссылку на загруженную страницу необходимо прописать
в[хеш-таблицу, чтобы в дальнейшем ее можно было найти.
7
Чтение в свободный буфер
свободные
буферы
хеш-таблица
1
3 1 1
следующая
жертва
1 0
В конце концов будет заполнен и последний свободный буфер.
Теперь ссылка на свободный буфер пуста — в дальнейшем, чтобы
прочитать новую страницу в любой буфер, придется из этого буфера
вытеснить страницу, которая там находится.
Буферы возвращаются в список свободных буферов только при
удалении или опустошении таблицы, или при отсечении части
хвостовых страниц при очистке — то есть в случаях, когда страница
в буфере не заменяется другой, а просто исчезает.
9
Чтение с вытеснением
свободные
буферы
хеш-таблица
11 1
следующая
жертва
2
–1
нельзя
вытеснять:
закреплен
1 0
Механизм вытеснения использует указатель на следующую «жертву».
Алгоритм состоит в том, чтобы перебирать по кругу все буферы,
уменьшая их счетчики обращений (usage count). Выбирается первый
же буфер, у которого значение счетчика равно 0 и который в принципе
может быть занят новой страницей. По-английски этот алгоритм
называется clock-sweep: аналогия с часовой стрелкой, которая
пробегает циферблат по кругу.
Чем[больше значение счетчика у буфера (то есть чем чаще он
используется), тем больше у него шансов задержаться в кеше.
Максимальное значение счетчика обращений ограничено числом 5,
чтобы избежать «наматывания кругов» при больших значениях.
В нашем примере процесс обращается к буферу по указателю. Буфер
закреплен (то есть используется каким-то процессом), и поэтому
страница не[может быть вытеснена из него. Тем не менее мы
уменьшаем значение счетчика обращений на единицу и переходим
к[следующему буферу.
10
Чтение с вытеснением
свободные
буферы
хеш-таблица
11 1
следующая
жертва
2
еще рано:
используется
активно
0 0
–1
Следующий буфер не закреплен, однако его счетчик обращений
больше нуля.
Мы уменьшаем счетчик на единицу и даем ему шанс остаться в кеше.
11
Чтение с вытеснением
свободные
буферы
хеш-таблица
11 1
следующая
жертва
2
можно
вытеснить
0 0
Наконец, мы приходим к незакрепленному буферу с нулевым счетчиком
обращений. Страница из этого буфера и будет вытеснена.
Однако в нашем примере найденный буфер оказался грязным — он
содержит измененные данные. Поэтому сначала страницу требуется
сохранить на диск. Для этого буфер закрепляется (чтобы показать
остальным процессам, что он используется), после чего страница
записывается на диск.
Это не очень хорошая ситуация: процессу, который собирался
прочитать страницу, придется ждать, пока «чужие» данные будут
записаны. Этот эффект сглаживается процессами контрольной точки
и фоновой записи, которые рассмотрены в теме «Контрольная точка».
12
Чтение с вытеснением
свободные
буферы
хеш-таблица
11 1
следующая
жертва
2
0 1
+1
После того как страница из грязного буфера записана на диск,
в[освободившийся буфер ранее рассмотренным образом читается
новая страница.
Ссылка на следующую «жертву» уже указывает на следующий буфер,
а[у[только что загруженного есть время нарастить счетчик обращений,
пока указатель не обойдет по кругу весь буферный кеш и не вернется
вновь.
13
Настройка размера кеша
Настройка
shared_buffers = 128MB
Буферный кеш должен содержать «активные» данные
при меньшем размере постоянно вытесняются полезные страницы
при большем размере бессмысленно растут накладные расходы
начальное приближение — 0.25 ОЗУ
Нужно учитывать двойное кеширование
если страницы нет в кеше СУБД, она может оказаться в кеше ОС
алгоритм вытеснения ОС не учитывает специфики базы данных
Размер кеша устанавливается параметром shared_buffers. Значение
по[умолчанию — 128 МБ — сильно занижено.
Как выбрать подходящее значение? Даже самая большая база имеет
ограниченный набор данных, с которыми ведется активная работа.
В[идеале этот набор (плюс некоторое место для «одноразовых»
данных) должен помещаться в буферный кеш.
При меньшем размере кеша активно используемые страницы будут
постоянно вытеснять друг друга, создавая избыточный ввод-вывод.
Признаком такой ситуации может служить то, что все страницы в кеше
имеют большое число обращений (usage count).
Однако при большом размере кеша будут расти накладные расходы
на его поддержание.
В качестве первого приближения можно взять 1/4 оперативной памяти,
но надо понимать, что оптимальное значение зависит не от размера
ОЗУ, а от конкретных данных и нагрузки.
Не следует забывать и о том, что PostgreSQL работает с диском через
операционную систему и, таким образом, происходит двойное
кеширование: страницы попадают как в буферный кеш, так и в кеш ОС.
Таким образом, «непопадание» в буферный кеш не всегда приводит
к[необходимости реального ввода-вывода. Но стратегия вытеснения ОС
не учитывает специфики баз данных.
15
Массовое вытеснение
Буферное кольцо
часть буферного кеша, выделенная для одной операции
предотвращает вытеснение кеша «одноразовыми» данными
кол-во
операция страниц грязные буферы
последовательное чтение 32 исключаются из кольца
очистка (VACUUM) 32 вытесняются на диск
массовая запись (COPY, CTAS) ≤2048 вытесняются на диск
При операциях, выполняющих массовое чтение или запись данных,
есть опасность быстрого вытеснения полезных страниц из буферного
кеша «одноразовыми» данными. Чтобы этого не происходило, для
таких операций используются так называемые буферные кольца — для
каждой операции выделяется небольшая часть буферного кеша.
При[этом вытеснение действует только в[пределах кольца, поэтому
остальные данные в буфере не страдают.
При последовательном чтении (sequential scan) больших таблиц
используется кольцо размером 32 страницы. Если в процессе
появляются грязные буферы (в результате простановки битов-подсказок
или при выполнении команды UPDATE), они отключаются от кольца,
возвращаются в[основной кеш[и вытесняются уже на общих
основаниях. Вместо отключенного буфера в кольцо из кеша
подключается другой буфер. Такая стратегия рассчитана на то, что
данные в основном читаются, а не меняются.
Если в процессе чтения таблицы другому процессу тоже потребуются
эти данные, он не начинает читать таблицу сначала, а подключается
к[уже имеющемуся буферному кольцу. После окончания сканирования
он дочитывает «пропущенное» начало таблицы.
Для процесса очистки также используется небольшое кольцо
(32[страницы), так как замедление фоновой задачи не столь критично,
как замедление пользовательских процессов.
Наоборот, для массовых операций записи — COPY, CREATE TABLE
AS[SELECT — кольцо имеет достаточно большой размер (обычно 2048
страниц, но не больше одной восьмой всего буферного кеша).