Архитектура
Буферный кеш и журнал
12
Авторские права
© Postgres Professional, 2017–2021
Авторы: Егор Рогов, Павел Лузанов
Использование материалов курса
Некоммерческое использование материалов курса (презентации,
демонстрации) разрешается без ограничений. Коммерческое
использование возможно только с письменного разрешения компании
Postgres Professional. Запрещается внесение изменений в материалы
курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу:
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за
любые повреждения и убытки, включая потерю дохода, нанесенные
прямым или непрямым, специальным или случайным использованием
материалов курса. Компания Postgres Professional не предоставляет
каких-либо гарантий на материалы курса. Материалы курса
предоставляются на основе принципа «как есть» и компания Postgres
Professional не обязана предоставлять сопровождение, поддержку,
обновления, расширения и изменения.
2
Темы
Устройство буферного кеша
Алгоритм вытеснения
Журнал предзаписи
Контрольная точка
3
Буферный кеш
Массив буферов
страница данных (8 Кбайт)
доп. информация
Блокировки в памяти
для совместного доступа
PostgreSQL
postmaster
backend
ОС
кеш
фоновые процессы
общая память
буферный кеш
Буферный кеш используется для сглаживания скорости работы
оперативной памяти и дисков. Он состоит из массива буферов, которые
содержат страницы данных и дополнительную информацию (например,
имя файла и положение страницы внутри этого файла).
Размер страницы обычно составляет 8 Кбайт (его можно изменить, но
только при сборке PostgreSQL, и обычно в этом нет смысла).
Любая работа со страницами данных проходит через буферный кеш.
Если какой-либо процесс собирается работать со страницей, он
в первую очередь пытается найти ее в кеше. Если там страницы нет,
процесс обращается к операционной системе с просьбой прочитать эту
страницу и помещает ее в буферный кеш. (Обратите внимание, что ОС
может прочитать страницу с диска, а может обнаружить ее
в собственном кеше.)
После того, как страница записана в буферный кеш, к ней можно
обращаться многократно без накладных расходов на вызовы ОС.
Однако буферный кеш, как и другие структуры общей памяти, защищен
блокировками для управления одновременным доступом. Хотя
блокировки и реализованы эффективно, доступ к буферному кешу
далеко не так быстр, как простое обращение к оперативной памяти.
Поэтому в общем случае чем меньше данных читает и изменяет
запрос, тем быстрее он будет работать.
4
Вытеснение
Вытеснение редко
используемых страниц
«грязный» буфер
записывается на диск
на освободившееся место
читается другая страница
PostgreSQL
postmaster
backend
фоновые процессы
общая память
буферный кеш
ОС
кеш
Размер буферного кеша обычно не так велик, чтобы база данных
помещалась в него целиком. Его ограничивают и доступная
оперативная память, и возрастающие при его увеличении накладные
расходы. Поэтому при чтении очередной страницы рано или поздно
окажется, что место в буферном кеше закончилось. В этом случае
применяется вытеснение страниц.
Алгоритм вытеснения выбирает в кеше страницу, которая в последнее
время использовалась реже других, и заменяет ее новой. Если
выбранная страница изменялась, то ее предварительно надо записать
на диск, чтобы не потерять изменения (буфер, содержащий
измененную страницу, называется «грязным»).
Такой алгоритм вытеснения называется LRU — Least Recently Used. Он
сохраняет в кеше данные, с которыми происходит активная работа.
Таких «горячих» данных обычно не так много, и при достаточном
объеме буферного кеша получается существенно сократить количество
обращений к ОС (и дисковых операций).
6
Журнал (WAL)
Проблема: при сбое теряются данные из оперативной
памяти, не записанные на диск
Журнал
поток информации о выполняемых действиях,
позволяющий повторно выполнить потерянные при сбое операции
запись попадает на диск раньше, чем измененные данные
Журнал защищает
страницы таблиц, индексов и других объектов
статус транзакций (xact)
Журнал не защищает
временные и нежурналируемые таблицы
Наличие буферного кеша (и других буферов в оперативной памяти)
увеличивает производительность, но уменьшает надежность. В случае
сбоя в СУБД содержимое буферного кеша потеряется. Если сбой
произойдет в операционной системе или на аппаратном уровне, то
пропадет содержимое и буферов ОС (но с этим справляется сама
операционная система).
Для обеспечения надежности PostgreSQL использует журналирование.
При выполнении любой операции формируется запись, содержащая
минимально необходимую информацию для того, чтобы операцию
можно было выполнить повторно. Такая запись должна попасть на диск
(или другой энергонезависимый накопитель) раньше, чем будут
записаны изменяемые операцией данные (поэтому журнал и
называется журналом предзаписи, write-ahead log).
Журнал защищает все объекты, работа с которыми ведется
в оперативной памяти: таблицы, индексы и другие объекты, статус
транзакций.
В журнал не попадают данные о временных таблицах (такие таблицы
доступны только создавшему их пользователю и только на время
сеанса или транзакции) и о нежурналируемых таблицах (такие
таблицы ничем не отличаются от обычных, кроме того, что не
защищены журналом). В случае сбоя такие таблицы просто очищаются.
Смысл их существования в том, что работа с ними происходит быстрее.
7
Контрольная точка
Периодический сброс всех грязных буферов на диск
гарантирует попадание на диск всех изменений до контрольной точки
ограничивает размер журнала, необходимого для восстановления
Восстановление при сбое
начинается с последней контрольной точки
последовательно проигрываются записи, если изменений нет на диске
xid
контрольная точка
контрольная точка сбой
необходимые файлы журнала
начало
восстановления
Когда сервер PostgreSQL запускается после сбоя, он входит в режим
восстановления. Информация на диске в это время несогласованна:
изменения «горячих» страниц пропали, поскольку эти страницы все
еще находились в кеше, хотя другие, более поздние изменения уже
были сброшены на диск.
Чтобы восстановить согласованность, PostgreSQL читает журнал WAL
и последовательно проигрывает каждую журнальную запись, если
соответствующее изменение не попало на диск. Таким образом
восстанавливается работа всех транзакций. Затем те транзакции,
запись о фиксации которых не успела попасть в журнал, обрываются.
Однако объем журнала за время работы сервера мог бы достигнуть
гигантских размеров. Хранить его целиком и целиком просматривать
при сбое совершенно не реально. Поэтому СУБД периодически
выполняет контрольную точку: принудительно сбрасывает на диск все
грязные буферы (включая буферы xact, в которых хранится состояние
транзакций). Это гарантирует, что изменения всех транзакций до
момента контрольной точки находятся на диске.
Контрольная точка может занимать много времени, и это нормально.
Собственно «точка», о которой мы говорим как о моменте времени —
это начало процесса. Но точка считается выполненной только после
того, как записаны все грязные буферы, которые имелись на момент
начала процесса.
Восстановление после сбоя начинается с ближайшей контрольной
точки, что позволяет хранить только файлы журнала, записанные
с момента последней пройденной контрольной точки.
9
Производительность
Синхронный режим
запись при фиксации
обслуживающий процесс
Асинхронный режим
фоновая запись
walwriter
PostgreSQL
backend
postmaster
checkpointer
фоновые процессы
walwriter
общая память
буферный кешxactwal
ОС
журнал
WAL
статус
транзакций
fsync
кеш
Механизм журналирования более эффективен, чем работа напрямую
с диском без буферного кеша. Во-первых, размер журнальных записей
меньше, чем размер целой страницы данных; во-вторых, журнал
записывается строго последовательно (и обычно не читается, пока не
случится сбой), с чем вполне справляются простые HDD-диски.
На эффективность можно также влиять настройкой. Если запись
происходит сразу (синхронно), то гарантируется, что зафиксированная
транзакция не пропадет. Но запись — довольно дорогая операция,
в течение которой обслуживающий процесс, выполняющий фиксацию,
вынужден ждать. Чтобы журнальная запись не «застряла» в кеше
операционной системы, выполняется вызов fsync: PostgreSQL
полагается на то, что это гарантирует попадание данных на
энергонезависимый носитель.
Поэтому есть и режим отложенной (асинхронной) записи. В этом случае
записи пишутся фоновым процессом walwriter постепенно, с небольшой
задержкой. Надежность уменьшается, зато производительность
увеличивается. В этом случае после сбоя также гарантируется
восстановление согласованности, но некоторые последние
зафиксированные транзакции могут пропасть.
10
Итоги
Буферный кеш существенно ускоряет работу,
уменьшая число дисковых операций
Надежность обеспечивается журналированием
Размер журнала ограничен благодаря контрольным точкам
Журнал удобен и используется во многих случаях
для восстановления после сбоя
при резервном копировании
для репликации между серверами
11
Практика
1. Проверьте, как используется буферный кеш в случае
обновления одной строки в обычной и во временной таблице.
Попробуйте объяснить отличие.
2. Создайте нежурналируемую таблицу и вставьте в нее
несколько строк. Сымитируйте сбой системы, остановив
сервер в режиме immediate, как в демонстрации.
Запустите сервер и проверьте, что произошло с таблицей.
Найдите в журнале сообщений сервера упоминание
о восстановлении после сбоя.
1. Временные таблицы выглядят так же, как обычные, но время их
жизни — текущий сеанс. Такая таблица видна тоже только в текущем
сеансе.
Воспользуйтесь командой
EXPLAIN (analyze, buffers, costs off, timing off)
как было показано в демонстрации.
2. Останов в режиме immediate выполняется так:
sudo pg_ctlcluster 12 main stop -m immediate --skip-systemctl-redirect
Ключ --skip-systemctl-redirect нужен здесь из-за того, что используется
PostgreSQL, установленный в Ubuntu из пакета. Он управляется
командой pg_ctlcluster, которая вызывает утилиту systemctl, которая
в свою очередь вызывает pg_ctl, теряя при этом указанный режим.
Ключ позволяет обойтись без systemctl и передать информацию pg_ctl.