- •Предисловие
- •Об этой книге
- •Глава 1. Обзор статистики
- •Внутреннее устройство PostgreSQL
- •Установка соединений и работа сеансов
- •Запросы как базовая единица рабочей нагрузки
- •Планирование и выполнение запросов
- •Ввод-вывод при выполнении запросов
- •Журнал сообщений СУБД
- •Репликация изменений
- •Архивирование журнала предзаписи
- •Фоновая синхронизация данных
- •Автоочистка
- •Интерфейс статистики
- •Статистика как отправная точка инструментов мониторинга
- •Особенности статистики
- •Тестовое окружение
- •Глава 2. Статистика активности
- •Ключ к пониманию происходящего в СУБД
- •Взаимодействие клиента и сервера
- •Источники информации об активности
- •Представление pg_stat_activity
- •Представление pg_locks
- •Особенности pg_stat_activity и pg_locks
- •Представление pg_stat_database
- •Подключенные клиенты
- •Отслеживание клиентских сеансов
- •Транзакционная активность
- •Статусы завершения сеансов
- •Состояния сеансов
- •Отслеживание состояний
- •Ожидания и блокировки
- •Отслеживание состояний с учетом ожиданий
- •Взаимоблокировки
- •Бездействующие транзакции
- •Время выполнения запросов и транзакций
- •Отслеживание времени ожидания блокировок
- •Использование pg_locks.waitstart
- •Использование pg_stat_activity.state_change
- •Дерево блокировок
- •Глава 3. Выполнение запросов и функций
- •Зачем нужен мониторинг запросов
- •Расширение pg_stat_statements
- •Метаданные запроса
- •Планирование запроса
- •Исполнение запроса
- •Сквозная идентификация с queryid
- •Построение отчетов на основе pg_stat_statements
- •Представление pg_stat_statements_info
- •Выполнение процедур и функций
- •Глава 4. Базы данных
- •Иерархия объектов СУБД
- •Кластер баз данных
- •Табличные пространства
- •Базы данных
- •Схемы
- •Таблицы и индексы
- •TOAST
- •События в кластере баз данных
- •Рабочая нагрузка в отношении таблиц и индексов
- •Ошибки и нежелательные события
- •Функции для работы с объектами СУБД
- •Определение размеров объектов СУБД
- •Размещение объектов в файловой системе
- •Глава 5. Область общей памяти и ввод-вывод
- •Анализ общей памяти
- •Представление pg_buffercache
- •Представление pg_shmem_allocations
- •Анализ памяти клиентских процессов
- •Оценка использования SLRU-кешей
- •Ввод-вывод в контексте объектов СУБД
- •Базы данных
- •Ввод-вывод в контексте выполнения запросов
- •Временные файлы
- •Уровень баз данных
- •Ввод-вывод при выполнении запросов
- •Отслеживание в журнале сообщений
- •Отслеживание активных временных файлов
- •Ввод-вывод фоновых процессов
- •Глава 6. Журнал упреждающей записи
- •Отслеживание активности в журнале
- •Представление pg_stat_wal
- •Представление pg_stat_statements
- •Архивирование журнала
- •Представление pg_stat_archiver
- •Очередь архивирования
- •Глава 7. Репликация
- •Обзор репликации
- •Инструменты отслеживания репликации
- •Представление pg_stat_replication
- •Представление pg_stat_wal_receiver
- •Cлоты репликации и pg_replication_slots
- •Публикации и подписки
- •Конфликты восстановления
- •Глава 8. Очистка
- •Введение в очистку
- •Особенности очистки на практике
- •Когда выполняется автоочистка?
- •Статистика выполнения очистки
- •Счетчик транзакций и предотвращение ошибок, связанных с его зацикливанием
- •Раздувание таблиц и индексов
- •Отслеживание активных процессов очистки
- •Представление pg_stat_activity
- •Представление pg_stat_progress_vacuum
- •Глава 9. Ход выполнения операций
- •Представление pg_stat_progress_analyze
- •Представление pg_stat_progress_basebackup
- •Представление pg_stat_progress_cluster
- •Представление pg_stat_progress_create_index
- •Представление pg_stat_progress_copy
- •Предметный указатель
Глава 6
Журнал упреждающей записи
Вэтой главе мы рассмотрим:
•что такое журнал упреждающей записи;
•устройство журнала упреждающей записи в PostgreSQL;
•как отслеживать активность,связанную с журналом;
•представление pg_stat_wal;
•отслеживание записи в журнал запросами;
•архивирование сегментов журнала;
•представление pg_stat_archiver;
•способы отслеживания проблем при архивировании журнала;
•отслеживание очереди архивирования журнала.
Вэтой главе мы рассмотрим журнал упреждающей записи, он же журнал предзаписи (WriteAhead Log), который в том или ином виде присутствует в большинстве СУБД. Журнал предзаписи в общем виде представляет собой историю всех изменений данных, происходящих в СУБД. В случае аварий такая история позволяет воспроизвести всю последовательность изменений до момента аварии и восстановить актуальное и согласованное состояние СУБД.
Вэтой главе мы кратко рассмотрим устройство журнала (здесь и далее в этой главе подтерми- ном«журнал»будетподразумеватьсяименноWAL-журнал,анежурналсообщенийСУБД)ибо- лее подробно рассмотрим инструменты и способы отслеживания событий, связанных с журналом.
6.1. Write-Ahead Log —журнал упреждающей записи
Для гарантий надежности все изменения данных в СУБД должны быть записаны в надежное хранилище (обычно на диск). Нельзя допускать искажений, повреждений и тем более потери данныхдажевслучаесбоеввсистеме.ДляреализацииэтихтребованийпрактическивсеСУБД используютжурнал предзаписи.В PostgreSQL журнал представляетсобой историю изменений данных,иСУБДвслучаеавариииспользуетжурналдлявоспроизведенияэтихизмененийидостижения последней согласованной точки до момента аварии.
Экземпляр СУБД состоит из множества клиентских и фоновых процессов, которые работают с данными, размещенными в общей памяти, — к таким данным относятся и буферный
158Глава 6. Журнал упреждающей записи
кеш с пользовательскими данными, и служебные структуры данных. Приложения отправляют запросы, а СУБД выполняет их. В случае запросов на чтение СУБД обращается к данным и возвращает необходимый набор строк; в случае запросов на изменение данных (или схемы) СУБД вносит соответствующие изменения.Все изменения происходят в общей памяти,затем в отложенном режиме процесс фоновой записи или контрольной точки синхронизирует изменения в кеше с основным хранилищем. Общая память СУБД является оперативной, и ее содержимое не защищено отсистемных сбоев вроде отказов питания.Также существуеткласс программных ошибок,при которых областьобщей памяти можетбытьповреждена и потребуется ее полная очистка и пересоздание—как правило,это ошибки сегментации (segmentation fault). К подобным последствиям приводит и аварийное завершение операционной системой каких-либо процессов СУБД.Обычнотакие аварийные ситуации происходятвнезапно,нельзя подготовиться к ним заранее и ждать их наступления в известный момент. Получается, что СУБД нужен инструмент, позволяющий восстановить те изменения данных, которые произошли в общей памяти,но из-за аварии не были записаны в основное хранилище.
Однако СУБД может эксплуатироваться очень долго, и хранить абсолютно всю историю изменений невозможно (либо это требует значительных или даже неадекватных экономических вложений). Для повторного использования файлов журнала и поддержания его в разумных объемах применяются контрольные точки: все изменения, предшествующие такой точке, гарантированно записаны в основное хранилище данных,которое считается надежным.Отметки об успешном завершении контрольных точек также записываются в журнал, после чего часть журнала до контрольной точки может быть удалена. В случае аварии остается воспроизвести только те изменения, которые были записаны в журнал после контрольной точки, и прийти к состоянию до момента аварии.
На практике журнал представляетсобой набор файлов (сегментов) внутри подкаталога pg_wal
восновном каталоге данных:
#ls -l /var/lib/postgresql/data/pg_wal/ total 196616
-rw------- |
1 |
postgres postgres |
341 |
Nov |
4 |
05:03 000000010000000000000011.0001F7A0.backup |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:26 000000010000004B000000C2 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:27 000000010000004B000000C3 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:28 000000010000004B000000C4 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:28 000000010000004B000000C5 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:29 000000010000004B000000C6 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:30 000000010000004B000000C7 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:31 000000010000004B000000C8 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:32 000000010000004B000000C9 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:24 000000010000004B000000CA |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:20 000000010000004B000000CB |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:25 000000010000004B000000CC |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
05:23 000000010000004B000000CD |
drwx------ |
2 |
postgres postgres |
4096 |
Nov |
18 |
05:31 archive_status |
Все сегменты журнала имеют имя из 24 цифр в шестнадцатеричном формате без расширения. Имя состоит из трех октетов (на примере сегмента 000000010000004B000000CD):
6.1. Write-Ahead Log—журнал упреждающей записи |
159 |
1.00000001— идентификатор линии времени (timeline id). Линии времени используется механизмом восстановления на точку во времени (Point-in-Time Recovery,PITR)1,2.
2.0000004B — идентификатор верхнего диапазона сегментов со значениями от 00000000
до FFFFFFFF.
3.000000CD — идентификатор нижнего диапазона сегментов со значениями от 00000000, а последний номер зависит от размера сегмента, заданного при инициализации основного каталога.
Каждый сегмент имеет фиксированный размер (см. wal_segment_size), по умолчанию 16 МБ. Внутри каждый сегмент поделен на блоки по 8 КБ (см. wal_block_size). Размер сегмента можно менять при инициализации кластера (параметр --wal-segsize). Каждый блок содержит WALзаписи(records)внепечатаемомдвоичномформате,описывающиеотдельныеизмененияданных. Одна такая запись содержит достаточно информации для воспроизведения (повторного применения) одного конкретного изменения в случае сбоя системы. Записи могут иметь разные типы и относиться к изменениям самых разных объектов. Для анализа содержимого журнала можно использовать утилиту pg_waldump3 или расширение pg_walinspect4. Оба инструмента предоставляютодинаковую функциональность,но первый предназначендля ра- ботытольковсредетерминала,арасширениепредоставляетSQL-интерфейссболеебогатыми возможностями для анализа результатов:
# SELECT * FROM pg_get_wal_records_info('4B/DF000000','4B/E0000000') LIMIT 14; |
|
start_lsn | end_lsn | prev_lsn | xid |
| resource_manager | record_type | record_length | main_data_length | fpi_length | |
-------------+-------------+-------------+----------+------------------+-------------+---------------+------------------+------------+-----------...
4B/DF000350 | 4B/DF0003F8 | 4B/DEFFE370 | 43601290 |
| Heap |
| HOT_UPDATE |
| |
163 |
| |
14 |
| |
0 |
| off 88 xma... |
|
4B/DF0003F8 | 4B/DF000448 | 4B/DF000350 | 43601290 |
| Heap |
| HOT_UPDATE |
| |
78 |
| |
14 |
| |
0 |
| off 22 xma... |
|
4B/DF000448 | 4B/DF000498 | 4B/DF0003F8 | 43601290 |
| Heap |
| HOT_UPDATE |
| |
74 |
| |
14 |
| |
0 |
| off 111 xm... |
|
4B/DF000498 | 4B/DF0004E8 | 4B/DF000448 | 43601290 |
| Heap |
| INSERT |
| |
79 |
| |
3 |
| |
0 |
| off 43 fla... |
|
4B/DF0004E8 | 4B/DF000518 | 4B/DF000498 | 43601290 |
| Transaction |
| COMMIT |
| |
46 |
| |
20 |
| |
0 |
| 2022-11-18... |
|
4B/DF000518 | 4B/DF0024E8 | 4B/DF0004E8 | |
0 |
| Heap2 |
| PRUNE |
| |
8119 |
| |
8 |
| |
8060 |
| latestRemo... |
4B/DF0024E8 | 4B/DF002590 | 4B/DF000518 | 43601291 |
| Heap |
| HOT_UPDATE |
| |
163 |
| |
14 |
| |
0 |
| off 104 xm... |
|
4B/DF002590 | 4B/DF0025E0 | 4B/DF0024E8 | 43601291 |
| Heap |
| HOT_UPDATE |
| |
78 |
| |
14 |
| |
0 |
| off 71 xma... |
|
4B/DF0025E0 | 4B/DF002630 | 4B/DF002590 | 43601291 |
| Heap |
| HOT_UPDATE |
| |
74 |
| |
14 |
| |
0 |
| off 133 xm... |
|
4B/DF002630 | 4B/DF002680 | 4B/DF0025E0 | 43601291 |
| Heap |
| INSERT |
| |
79 |
| |
3 |
| |
0 |
| off 27 fla... |
|
4B/DF002680 | 4B/DF0026B0 | 4B/DF002630 | 43601291 |
| Transaction |
| COMMIT |
| |
46 |
| |
20 |
| |
0 |
| 2022-11-18... |
|
4B/DF0026B0 | 4B/DF004680 | 4B/DF002680 | |
0 |
| Heap2 |
| PRUNE |
| |
8119 |
| |
8 |
| |
8060 |
| latestRemo... |
4B/DF004680 | 4B/DF004728 | 4B/DF0026B0 | 43601292 |
| Heap |
| HOT_UPDATE |
| |
163 |
| |
14 |
| |
0 |
| off 78 xma... |
|
4B/DF004728 | 4B/DF004778 | 4B/DF004680 | 43601292 |
| Heap |
| HOT_UPDATE |
| |
78 |
| |
14 |
| |
0 |
| off 179 xm... |
|
Каждая отдельная запись имеет уникальный идентификатор LSN (log sequence number).Идентификатор можно использовать как позицию в WAL-журнале, по которой можно определить местоположение записи в журнале.На примере записи 4B/DF0026B0:
•4B—идентификатор верхнего диапазона (второй октет имени файла сегмента);
•DF—идентификатор нижнего диапазона (третий октет имени файла сегмента);
•0026B0—смещение внутри файла сегмента.
1 www.interdb.jp/pg/pgsql10.html#_10.3.1.
2 postgrespro.ru/docs/postgresql/current/continuous-archiving
3 postgrespro.ru/docs/postgresql/current/pgwaldump
4 postgrespro.ru/docs/postgresql/current/pgwalinspect
160Глава 6. Журнал упреждающей записи
Определить сегмент, в котором находится конкретная запись, можно с помощью функции pg_walfile_name,передав ей LSN:
# SELECT pg_walfile_name('4B/DF008B78'); pg_walfile_name
--------------------------
000000010000004B000000DF
При активной эксплуатации СУБД и постоянном измененииданных в журнал вставляются новые записи итекущая позиция (выраженная в LSN) постоянно смещается вперед.После вставкидобавленныезаписиследуетнадежнозаписатьивосновноехранилище.Получитьтекущую позицию записи журнала можно с помощью нескольких функций:
•pg_current_wal_insert_lsn—позиция последней вставленной в журнал записи;
•pg_current_wal_lsn—позиция последней сохраненной на диск записи;
•pg_current_wal_flush_lsn — позиция последней сохраненной и синхронизированной с диском записи.
При полном заполнении сегмента вставка начинается в следующий по порядку сегмент.
Объем WAL-журнала ограничен параметрами конфигурации и при нормальной эксплуатации количество сегментов колеблется в определенном диапазоне. При пиковых нагрузках, особенно связанных с изменением данных, могут создаваться дополнительные сегменты, отчего размер журнала может увеличиваться. При возвращении нагрузки к нормальным значениям и после выполнения контрольной точки размер журнала скорректируется — излишние сегменты будут либо удалены, либо переименованы для использования в будущем; таким образом объем журнала уменьшится до размеров, заданных в конфигурации СУБД. В следующем листинге по отметкам времени можно заметить, что запись идет в сегмент 000000010000004B000000FC, а сегменты с бóльшими номерами, но меньшим временем зарезервированы под использование в ближайшем будущем — как только активный сегмент будет заполнен,СУБД переключится на запись в следующий сегмент.
# ls -l /var/lib/postgresql/data/pg_wal/ |
|
|
|
|||
total 196616 |
|
|
|
|
|
|
-rw------- |
1 |
postgres postgres |
341 |
Nov |
4 |
05:03 000000010000000000000011.0001F7A0.backup |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:21 000000010000004B000000F5 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:22 000000010000004B000000F6 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:23 000000010000004B000000F7 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:24 000000010000004B000000F8 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:25 000000010000004B000000F9 |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:26 000000010000004B000000FA |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:27 000000010000004B000000FB |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:28 000000010000004B000000FC <<< активный сегмент |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:17 000000010000004B000000FD |
-rw------- |
1 |
postgres postgres |
16777216 |
Nov |
18 |
06:16 000000010000004B000000FE |
