11 Занятие 11: Журналирование / Настройка журнала
Эта страница извлечена из официального PDF в нативный Markdown. Текст слайдов, код и комментарии сохранены в порядке чтения; исходные PDF и HTML остаются эталоном макета. HTML source: dba2_11_wal_tuning.html Archive: DBA2-handouts-16.zip Страниц PDF: 22
Page 1: Журналирование / Настройка журнала
Авторские права
© Postgres Professional, 2016–2025 Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Игорь Гнатюк Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
Использование материалов курса
Некоммерческое использование материалов курса (презентации, демонстрации) разрешается без ограничений. Коммерческое использование возможно только с письменного разрешения компании Postgres Professional. Запрещается внесение изменений в материалы курса.
Обратная связь
Отзывы, замечания и предложения направляйте по адресу: edu@postgrespro.ru
Отказ от ответственности
Компания Postgres Professional не несет никакой ответственности за любые повреждения и убытки, включая потерю дохода, нанесенные прямым или непрямым, специальным или случайным использованием материалов курса. Компания Postgres Professional не предоставляет каких-либо гарантий на материалы курса. Материалы курса предоставляются на основе принципа «как есть» и компания Postgres Professional не обязана предоставлять сопровождение, поддержку, обновления, расширения и изменения.
Page 2: Темы
- Уровни журнала и решаемые задачи
- Надежность записи
- Производительность
Page 3: Уровни журнала
Настройка
- чем выше уровень,
- тем больше информации
- wal_level = replica
- в журнале
- Minimal
- восстановление после сбоя
- Replica
- восстановление из резервной копии, физическая репликация
- операции массовой обработки данных, блокировки,
- номера выполняющихся транзакций
- Logical
- логическая репликация
- информация для логического декодирования
Комментарии преподавателя
Кроме основной задачи — восстановления согласованности после сбоя — журнал можно использовать и для других целей, если добавить в него дополнительную информацию. Для этого существуют несколько уровней журнала, устанавливаемых параметром wal_level.
Уровень minimal содержит только информацию, необходимую для восстановления после сбоя. В журнал не записываются операции массовой обработки данных (такие, как CREATE TABLE AS SELECT, CREATE INDEX и т. п.), их долговечность гарантируется немедленной записью данных на диск.
Уровень replica (используется по умолчанию) позволяет восстанавливать систему из горячих резервных копий, сделанных утилитой pg_basebackup. Для этого в журнал записываются все изменения данных, включая операции массовой обработки. Уровень также позволяет организовать физическую репликацию — передавать поток журнальных записей на другой сервер (рассматривается в курсе DBA3), для чего в журнал включается информация о ряде блокировок.
Уровень logical используется для логической репликации — он должен быть включен на сервере, публикующем изменения. Для правильной работы логического декодирования в журнал записывается дополнительная информация, позволяющая корректно расшифровать смысл операций по журнальным записям.
Чем выше уровень, тем больше информации попадает в журнал и, следовательно, тем больше его объем.
Page 4: Уровни журнала
Используем новую базу данных.
Установим для журнала уровень minimal (при этом придется также задать нулевое значение параметра max_wal_senders и перезапустить сервер).
Посмотрим, как записывается в журнал команда CREATE TABLE AS SELECT.
Объем журнала:
В журнале нет записей, описывающих вставку строк в таблицу:
На уровне minimal изменения, выполненные операторами CREATE TABLE AS SELECT, CREATE INDEX, TRUNCATE и некоторыми другими, не журналируются. Эти операторы всегда сами выполняют синхронизацию, обеспечивая долговечность. А журнал содержит только записи, необходимые для восстановления после сбоя.
Page 5: Page 5
Удалим таблицу, вернем уровень по умолчанию (replica) и повторим эксперимент.
Посмотрим, как теперь записывается в журнал команда CREATE TABLE AS SELECT.
Объем журнала увеличился:
Помимо изменений системного каталога, в журнал попадают записи: CREATE — создание файла отношения; INSERT+INIT — вставка строк в таблицу; COMMIT — фиксация транзакции.
Page 6: Page 6
На уровне replica журнал содержит все изменения данных, что позволяет применить их к физической резервной копии или к реплике.
Page 7: Надежность записи
Кеширование
- Повреждение данных
- Неатомарность записи страниц
Page 8: Кеширование
{#source-figure-008 caption=“Source slide page 8: Кеширование”}
Синхронизация с диском
- данные должны дойти до энергонезависимого
- хранилища через многочисленные кеши
- СУБД сообщает о синхронизации операционной системе
- способом, указанным в wal_sync_method
- надо учитывать аппаратное кеширование
- Настройки
- fsync = on
- wal_sync_method
- (утилита pg_test_fsync)
Комментарии преподавателя
Есть несколько моментов, которые влияют на надежность. Во-первых, всевозможные кеши, находящиеся на пути к энергонезависимому хранилищу (такому как пластина жесткого диска).
Когда PostgreSQL требуется надежно записать данные, он пользуется способом, указанным в параметре wal_sync_method. Вариантов несколько, но основных — два: либо после записи операционной системе дается команда синхронизации (fsync, fdatasync), либо файл открывается или записывается со специальным флагом, который говорит о необходимости синхронизации, а по возможности — прямой записи, минуя кеш ядра ОС. Утилита pg_test_fsync позволяет выбрать способ, наиболее подходящий для конкретной ОС и конкретной файловой системы. Но в любом случае это дорогостоящая операция.
Кроме этого, администратор должен гарантировать, что данные действительно доходят до диска, а не задерживаются в кеше контроллера или самого диска. Если кеш контроллера откладывает запись, то контроллер в обязательном порядке должен иметь батарею резервного питания. Для дисков лучше устанавливать сквозную запись.
Вообще говоря, синхронизацию можно отменить (параметр fsync), но в этом случае про надежность хранения следует забыть. Единственный разумный вариант отключения этого параметра — временное увеличение производительности в случае, если данные можно легко восстановить (например, при начальной миграции).
https://postgrespro.ru/docs/postgresql/16/wal-reliability
Page 9: Повреждение данных
Контрольные суммы журнальных записей
- включены всегда, CRC-32
- Контрольные суммы страниц
- нужно включить при инициализации кластера
- накладные
- расходы, увеличение
- размера журнала
- Настройки
- data_checksums
- ignore_checksum_failure = off
Комментарии преподавателя
Во-вторых, данные могут быть повреждены на носителе, при передаче данных по интерфейсным кабелям и т. п. Часть таких ошибок обрабатывается на аппаратном уровне, но часть — нет.
Чтобы вовремя обнаружить возникшую проблему, журнальные записи всегда снабжаются контрольными суммами.
Страницы данных также можно защитить контрольными суммами. Это лучше сделать сразу при инициализации кластера, но можно включить утилитой pg_checksums и потом, остановив сервер. В производственной среде контрольные суммы должны быть включены обязательно, несмотря на накладные расходы на их вычисление и контроль. Иначе можно получить ситуацию, когда возникший сбой не будет вовремя обнаружен.
Проверить, включены ли контрольные суммы, можно с помощью параметра data_checksums (только для чтения). Параметр ignore_checksum_failure позволяет не прерывать транзакцию, прочитавшую сбойную страницу, но обычно его не следует включать.
Page 10: Неатомарность записи
Образ страницы
- при сбое страница может быть записана не полностью
- в журнал записывается образ страницы
- при первом ее изменении после контрольной точки
- при восстановлении журнальные записи
- применяются к записанному образу
- Настройки
- увеличивает
- размер журнала
- full_page_writes = on
- wal_compression = off
Комментарии преподавателя
В-третьих, есть проблема атомарности записи.
Страница данных занимает 8 КБ (или больше: 16 КБ, 32 КБ), а на низком уровне запись происходит блоками, которые обычно имеют меньший размер (512 байт, 4 КБ, хотя бывают и другие размеры). Поэтому при сбое питания страница данных может записаться частично. Понятно, что при восстановлении бессмысленно применять к такой странице обычные журнальные записи.
Для защиты PostgreSQL позволяет записывать в журнал образ всей страницы при первом ее изменении после контрольной точки — этим управляет параметр full_page_writes. Отключать его имеет смысл, только если используемая файловая система и аппаратура сами по себе гарантируют атомарность записи.
Если при восстановлении мы встречаем в журнале образ страницы, то безусловно записываем его на диск (к нему больше доверия, так как он, как и всякая журнальная запись, защищен контрольной суммой). И далее к нему уже применяем обычные журнальные записи.
Хотя PostgreSQL исключает из полного образа страницы незанятое место, все же объем журнальных записей увеличивается. Если в кластере включены контрольные суммы страниц, при изменении битов-подсказок в журнале появляется дополнительная запись, отражающая изменение контрольной суммы.
Размер журнала можно уменьшить за счет сжатия полных образов, задав метод сжатия параметром wal_compression. Поддерживаются методы pglz, lz4, zstd. Значение on соответствует выбору pglz, off отключает сжатие.
Page 11: Контрольные суммы
Создадим еще одну таблицу:
Вот файл, в котором находятся данные:
Остановим сервер и поменяем несколько байтов в странице (сотрем из заголовка LSN последней журнальной записи).
Можно было бы и не останавливать сервер. Достаточно, чтобы: страница записалась на диск и была вытеснена из кеша; произошло повреждение; страница была прочитана с диска. Теперь запускаем сервер.
Попробуем прочитать таблицу:
Параметр ignore_checksum_failure позволяет попытаться все-таки прочитать таблицу, хоть и с риском получить искаженные данные (например, если нет резервной копии):
Page 12: Производительность
Характер нагрузки
- Синхронная запись
- Асинхронная запись
Page 13: Характер нагрузки
Постоянный поток записи
- последовательная запись, отсутствие случайного доступа
- характер нагрузки отличается от остальной системы
- при высокой нагрузке — размещение на отдельных физических дисках
- (символическая ссылка из PGDATA/pg_wal)
- Редкое чтение
- при восстановлении
- при работе процессов walsender, если реплика не успевает быстро
- получать записи
Комментарии преподавателя
При обычной работе происходит постоянная и последовательная запись журнальных файлов. Поскольку отсутствует случайный доступ, с такой нагрузкой отлично справляются и обычные диски HDD.
Но такой характер нагрузки существенно отличается от того, как происходит доступ к файлам данных. Поэтому обычно выгодно размещать журнал на отдельном физическом диске (дисках), примонтированных к ФС сервера; вместо каталога PGDATA/pg_wal нужно создать символическую ссылку на соответствующий каталог.
Однако есть ситуация, при которой журнальные файлы необходимо читать (кроме понятного случая восстановления после сбоя). Она возникает, если используется потоковая репликация и реплика не успевает получать журнальные записи, пока они еще находятся в буферах оперативной памяти основного сервера. Тогда процессу walsender приходится читать нужные данные с диска. Взаимодействие с репликой подробно рассматривается в курсе DBA3 в модуле «Репликация».
Для мониторинга и оценки производительности работы WAL в PostgreSQL версии 14 было добавлено представление pg_stat_wal.
Page 14: Синхронная запись
Алгоритм
- при фиксации изменений сбрасываются все накопившиеся записи,
- включая запись о фиксации
- ожидание commit_delay, если активно не менее commit_siblings
- транзакций
- Свойства
- гарантируется долговечность
- увеличивается время отклика
- Настройки
- включать
- при большом потоке
- коротких транзакций
- synchronous_commit = on
- commit_delay
- = 0
- commit_siblings
- = 5
Комментарии преподавателя
Запись журнала происходит в одном из двух режимов: синхронном (когда при фиксации транзакции продолжение работы невозможно до тех пор, пока все журнальные записи о транзакции не окажутся на диске) и асинхронном (когда журнал записывается частями в фоне).
Синхронный режим включается параметром synchronous_commit.
Поскольку синхронизация работает долго, выгодно выполнять ее как можно реже. Для этого процесс, записывающий журнал, делает паузу, определяемую параметром commit_delay, но только в том случае, если имеется не менее commit_siblings активных транзакций — в расчете на то, что за время ожидания часть транзакций успеет завершиться и можно будет синхронизировать их записи за один раз.
Затем процесс выполняет сброс журнала на диск до необходимого LSN (или несколько больше, если за время ожидания добавились новые записи).
При синхронной записи гарантируется долговечность — если транзакция зафиксирована, то все ее журнальные записи уже есть на диске и не будут потеряны. Обратная сторона состоит в том, что синхронная запись увеличивает время отклика (команда COMMIT не возвращает управление до окончания синхронизации) и снижает производительность системы.
Page 15: Асинхронная запись
Алгоритм
- циклы записи через wal_writer_delay
- записываются только целиком заполненные страницы;
- но если новых полных страниц нет, то записывается текущая
- Свойства
- гарантируется согласованность, но не долговечность
- зафиксированные изменения могут пропасть (3 × wal_writer_delay)
- Настройки
- можно
- изменять на уровне
- synchronous_commit
- wal_writer_delay = 200ms
- wal_writer_flush_after = 1MB
- транзакции
Комментарии преподавателя
При асинхронной записи работает процесс wal writer, сбрасывая накопившиеся журнальные записи либо через wal_writer_delay единиц времени, либо по достижении объема wal_writer_flush_after:
● Если с прошлого раза в буферах была целиком заполнена одна или несколько страниц, сбрасываются только такие, полностью заполненные, страницы (или часть таких страниц; вспомним, что журнальный кеш представляет собой кольцевой буфер — записывается только непрерывная последовательность страниц). При большом потоке изменений это позволяет не синхронизировать одну и ту же страницу несколько раз.
● Если же заполненные страницы не появились, записывается текущая (не до конца заполненная) страница журнала.
Асинхронная запись эффективнее синхронной — фиксация изменений не ждет записи. Однако надежность уменьшается: зафиксированные данные могут пропасть в случае сбоя, если между фиксацией и сбоем прошло менее 3 × wal_writer_delay единиц времени.
Параметр synchronous_commit можно устанавливать в рамках транзакций. Это позволяет увеличивать производительность, жертвуя надежностью только части транзакций.
https://postgrespro.ru/docs/postgresql/16/wal-async-commit
В реальности оба режима работают совместно. Журнальные записи долгой транзакции будут записываться асинхронно (чтобы освободить буферы WAL). А если при сбросе грязного буфера окажется, что соответствующая журнальная запись еще не на диске, она тут же будет сброшена в синхронном режиме.
Page 16: Влияние синхронной фиксации на производительность
Режим, включенный по умолчанию, — синхронная фиксация.
Запустим простой тест производительности с помощью утилиты pgbench. Для этого сначала инициализируем необходимые таблицы…
…а также сбросим статистику о работе журнала предзаписи:
Запускаем тест на 10 секунд.
В результатах pgbench нас интересует число транзакций или скорость (tps), а в данных представления pg_stat_wal — количество операций записи и синхронизации журнала:
Page 17: Page 17
Теперь установим асинхронный режим.
Сбросим накопленные данные.
И снова запускаем тест.
Кроме повышения tps, мы видим и саму причину повышения производительности: количество операций синхронизации журнала значительно уменьшилось. Разумеется, на реальной системе соотношение может быть другим, но видно, что в асинхронном режиме производительность существенно выше.
Page 18: Итоги
Журнал позволяет восстановить согласованность после сбоев, но может использоваться и для других задач
- Надежность записи требует настройки не только СУБД, но и на уровне ОС, ФС и аппаратуры
- Настройка позволяет достичь баланса между долговечностью и производительностью
Page 19: Практика
- Изучите, как влияет на размер журнальных записей значение параметра full_page_writes.
- Для этого повторите простой тест pgbench, показанный
- в демонстрации, с разными настройками журнала. Перед
- запуском каждого теста выполняйте контрольную точку.
- Объясните полученный результат.
- Во сколько раз уменьшается размер журнальных записей при включении параметра wal_compression?
Комментарии преподавателя
Для детального анализа может пригодиться функция pg_get_wal_stats из расширения pg_walinspect.
Page 20: 1a. Full page writes = on
Запускаем тест на 10 секунд.
Смотрим статистику журнальных записей:
Page 21: Page 21
Значительную часть объема журнала составляют полные образы страниц (FPI).
1b. Full page writes = off
Запускаем тест на 10 секунд.
Смотрим статистику журнальных записей:
Объем заметно сократился, полные образы страниц в журнал теперь не записываются.
Page 22: 2. Сжатие
Запускаем тест на 10 секунд.
Смотрим статистику журнальных записей:
В данном случае — при наличии большого числа полных образов страниц — размер журнальных записей уменьшился в несколько раз. Хотя сжатие и нагружает процессор, практически наверняка им стоит воспользоваться при включенных полных образах страниц.