Какие есть виды записи wal файлов на диск синхронный
Рассмотрим технологии которые обеспечивают высокую производительность и безопасность работы сервера PostgreSQL, а именно “Буферный кэш” и “Журнал предзаписи WAL“. В документации об этом можете почитать тут.
Процессы, связанные с буферным кэшем и журналом
- WALWRITER – процесс записи журнала, обеспечивает асинхронный режим.
- CHECPOINT – процесс контрольной точки.
- BGWRITER – смотрит какие страницы скоро будут вытеснены из буферного кэша и скидывает грязные страницы на диск, делая их чистыми.
- BACKEND – обслуживающий процесс с которым работает приложение. При чтении из кэша иногда нужно вначале вытеснить одну страницу и поместить туда другую. Так как BGWRITER записывает такие страницы заранее, то BACKEND уже видит чистую страницу и без записи её на диск меняет на другую. Но если BGWRITER не успеет, то на себя эту работу возьмет BACKEND.
Практика
Журнал предзаписи (WAL)
То что у нас данные находятся в оперативной памяти – это хорошо. Но при сбое эти данные теряются, если не успели записаться на диск.
После сбоя наша база становится рассогласованной. Какие-то страницы менялись и были записаны, другие не успели записаться.
Журнал предварительной записи (WAL) – механизм, которым позволит нам восстановить согласованность данных после сбоя.
Когда мы хотим поменять какую-то страницу памяти, мы предварительно пишем, что хотим сделать в журнал предзаписи. И запись в этом журнале должна попасть на диск раньше, чем сама страница будет записана на диск.
В случае сбоя мы смотрим в журнал предзаписи и видим какие действия выполнялись, проделываем эти действия заново и восстанавливаем согласованность данных.
Почему запись в WAL эффективнее чем запись самой страницы? При записи страницы памяти на диск она пишется в произвольное место, это место еще нужно выбрать, подготовить для записи и начать запись. А запись в журнал ведется одним потоком и с этим потоком нормально справляются и обычные жёсткие диски и ssd.
WAL защищает всё что попадает в буферный кэш:
- страницы таблиц, индексов;
- статусы транзакций.
WAL не защищает:
- временные таблицы;
- нежурналируемые таблицы.
Задачи и виды репликации
Репликация PostgreSQL решает две задачи:
- отказоустойчивость – если сломается один из серверов, клиенты могут продолжить работать на резервном;
- масштабируемость – резервный сервер принимает запросы на чтение, так что некоторую нагрузку можно возложить на него.
Репликация на серверах PostgreSQL бывает двух видов:
- Физическая – основной сервер передает поток wal записей на сервер репликации. Требования следующие:
- одинаковые версии PostgreSQL;
- одинаковые ОС;
- возможна репликация только всего кластера.
- оба сервера могут быть и поставщиком и подписчиком, но на разные объекты. Например на в одном кластере опубликована одна табличка, а на другом – другая, и эти кластера подписаны друг на друга (на эти таблички). Это позволяет использовать двухсторонний обмен;
- репликация возможна между разными ОС;
- возможна выборочная репликация отдельных объектов кластера.
Уровни журналов
- Minimal – гарантия восстановления после сбоя.
- Replica (по умолчанию) – используется для резервного копирования и для репликации.
- Logical – используется для логической репликации.
Физическая репликация
После предыдущего урока у нас два каталога PGDATA. При этом второй кластер выключен.
Очистим каталог /usr/local/pgsql/data2/ и заново сделаем туда резервную копию с помощью pg_basebackup. Ключ -R подготовит архивную копию к дальнейшей репликации (создаст файл standby.signal):
Помимо standby.signal в postgresql.auto.conf были внесены параметры для последующей репликации:
Был добавлен параметр primary_conninfo, в котором указаны опции подключения к основному серверу:
- port=5432
- sslmode=disable
- и другие.
Вот ещё пример, который не нужно выполнять!
Задаются параметры подключения к основному серверу с помощью опций pg_basebackup. Например мы подключались бы к другому серверу, а для репликации был бы отдельный пользователь, тогда команда выглядела бы так:
В команде выше мы используем следующие опции:
- -h – хост основного сервера;
- -D – каталог PGDATA на основном сервере;
- -R – создает файл standby.signal;
- -P – включает отчет о прогрессе;
- -U – указываем пользователя для подключения;
- -X stream – используется для включения необходимых wal файлов в резервную копию, stream означает потоковую передачу WAL.
После такой команды, конфигурационный файл postgresql.auto.conf будет содержать такой параметр подключения к основному серверу:
Поменяем порт второго кластера, чтобы он мог работать одновременно с первым:
Файл standby.signal который появился во втором кластере означает настройку standby_mode=on в recovery.conf до PostgreSQL 12. То есть ничего делать не нужно для того чтобы перевести сервер в режим работы реплики.
Можем запустить второй кластер:
Посмотрим на процессы реплики. Процесс walreceiver streaming принимает поток wal записей, а процесс startup recovering применяет изменения:
В основном кластере появился процесс walsender postgres, который передаёт wal записи:
Проверить состояние репликации можно в представлении pg_stat_replication на главном сервере:
Внесём некоторые изменения на мастере:
Проверим, создались ли эти объекты на сервере репликации:
Сама реплика ничего менять не может:
Переведём реплику в обычный режим, то есть отключим репликацию. Это делается с помощью команды promote. При этом у нас пропадет файлик standby.signal.
После этого второй кластер может писать данные:
Алгоритм вытеснения
Чтобы буферный кэш не переполнился нужно редко используемые страницы из него вытеснять. Другими словами удалить из буфера. Если мы страничку поменяли, то она считается грязной, и перед вытеснением из буфера её нужно записать на диск. Если мы страничку не меняли то и записывать на диск её ещё раз не имеет смыла.
В PostgreSQL реализован алгоритм вытеснения страниц из буфера при котором в кэше остаются самые часто используемые страницы.
Устройство буферного кэша
Кэш нужен чтобы читать востребованные данные ни с диска а с более быстрой оперативной памяти. Предварительно данные приходится загружать с диска в буферных кэш оперативной памяти.
Есть 3 варианта размера страниц:
Буферный кэш занимает большую часть общей памяти.
Если процессу понадобятся какие-то данные он их вначале ищет в буферном кэше. Если данных в кэше не оказалось, то мы просим операционную систему прочитать эту страничку и поместить в буферный кэш. Операционная система имеет свой дисковые кэш, и ищет эту страничку там, если не находит, то читает с диска и помещает в дисковый кэш. Затем из дискового кэша страничка помещается в буферный кэш для PostgreSQL.
Так как буферный кэш находится в общей памяти, то чтобы разные процессы не мешали друг другу, к нему применяют различные блокировки.
Сценарии использования физической репликации
- обычная репликация – для создания резервного сервера;
- каскадная репликация – к основному серверу подключаем реплику, а к этой реплики еще одну реплику;
- отложенная репликация – в recovery.conf специальным параметром можно указать задержку воспроизведения. Чтобы реплика всегда отставала от основного сервера, например, на час.
База данных PostgreSQL, серия пятая: журнал упреждающей записи WAL
[Введение в WAL]
Ведение журнала с упреждающей записью - это стандартный метод обеспечения целостности данных. Проще говоря, концепция WAL заключается в том, что изменения файлов данных (включая таблицы и индексы) должны быть сначала записаны в журнал, то есть записи журнала могут быть записаны после того, как они будут сброшены в постоянное хранилище. После этого процесса нет необходимости сбрасывать страницы данных на диск при фиксации каждой транзакции, потому что журналы могут использоваться для восстановления базы данных во время простоя: любые изменения, которые не применяются к страницам данных, могут быть повторены на основе записей журнала. (То есть откат и восстановление РЕДО)
Для PostgreSQL перед использованием механизма WAL, если база данных выйдет из строя, может возникнуть риск неполных страниц данных.WAL сохраняет содержимое всей страницы данных в журнале, что отлично решает эту проблему.
Основная идея WAL состоит в том, чтобы сначала записать журнал, а затем записать данные. Модификация файла данных должна происходить после того, как эти изменения были записаны в файл журнала.
Когда происходит изменение:
- Записать измененное содержимое в буфер WAL
- Записать обновленные данные в буфер данных
Когда происходит отправка фиксации:
- Сбросить буфер WAL на диск
- Запись буфера данных на диск отложена
Когда возникает контрольная точка:
[Преимущество WAL]
В случае простоя- Содержимое буфера данных не было записано в постоянное хранилище, и данные потеряны;
- Содержимое буфера WAL было записано на диск. В соответствии с содержимым журнала WAL потерянное содержимое библиотеки может быть восстановлено.
Во время отправки на диск сбрасывается только WAL, а не данные:
- Что касается количества операций ввода-вывода, обновление WAL - это небольшой объем операций ввода-вывода, обновление данных - это большой объем операций ввода-вывода, а обновления WAL выполняются гораздо реже;
- С точки зрения затрат на ввод-вывод, обновление WAL - это непрерывный ввод-вывод, обновление данных - это случайный ввод-вывод, а обновление WAL намного дешевле.
Таким образом, механизм WAL успешно улучшил производительность системы, обеспечивая при этом надежность транзакций и целостность данных.
[Связанные понятия]
Redo Log
Журнал повторов обычно называется журналом повторов. Перед записью в файл данных каждое изменение будет записано в журнал повторов. Его цель и значение - хранить всю историю изменений базы данных для восстановления после сбоя (восстановление), инкрементного резервного копирования (инкрементное резервное копирование), PITR (восстановление на момент времени) и репликации (репликация).Redo Point
Точка REDO - это начальная точка для PG начала восстановления. Это конец файла журнала транзакций, когда была запущена последняя контрольная точка, то есть позиция смещения при записи записи XLOG контрольной точки.XLOG Record
Каждая операция изменения в PostgreSQL - это запись XLOG, которая хранится в файле сегмента WAL. PG будет читать эти записи XLOG для выполнения восстановления после сбоев / PITR и других операций.WAL Buffer
Буфер WAL, будь то заголовок файла сегмента WAL или запись XLOG, сначала будет записан в буфер WAL, а затем записан в файл сегмента WAL через модуль записи WAL «при необходимости» в. Если буфер слишком мал, будет потребляться больше ресурсов ввода-вывода. Если буфер слишком велик, существует риск потери данных после сбоя питания. Рекомендуется использовать значение по умолчанию, равное 1/32 shared_buffers.LSN
LSN - порядковый номер журнала. Указывает место в журнале транзакций, куда записываются записи XLOG. Значение LSN - это 64-битное целое число без знака (uint64). В журнале транзакций LSN монотонно увеличивается и уникален.
LSN состоит из 3 частей: логического идентификатора файла, физического идентификатора файла и смещения внутри файла. Например, LSN: 1 / 4288E228, где 1 - логический идентификатор файла, 42 - физический идентификатор файла, а 88E228 - смещение в файле сегмента WAL (примечание: пространство поиска для 3 байтов составляет 16 МБ). Соответствующий файл журнала сегментов WAL можно получить через LSN следующим образом:Checkpointer
Контрольная точка - это фоновый процесс в PG, который периодически выполняет контрольную точку. Когда контрольная точка выполняется, процесс записывает запись XLOG, содержащую информацию о контрольной точке, в текущий файл сегмента WAL.Запись записи XLOG содержит позицию последней пинты Redo.Checkpoint
Контрольная точка выполняется процессом контрольной точки, и основной поток обработки выглядит следующим образом:
(1) Получите точку повтора, создайте запись XLOG, содержащую информацию о контрольной точке этой точки повтора, и запишите ее в файл сегмента WAL;
(2) Обновите грязную страницу на диске;
(3) Обновить точку повтора и другую информацию в файле pg_control;PG Control
pg_control - это физический файл на диске. Он сохраняет основную информацию о контрольных точках и используется при восстановлении базы данных. Вы можете просмотреть содержимое файла с помощью команды pg_controldata.Описание параметра:
① Необязательные значения параметра wal_level - минимальные, реплики и логические. Уровень wal в свою очередь увеличивается, и чем больше информации содержится в wal. Краткое описание:- минимальный => База данных не может быть восстановлена через базовое резервное копирование и журналы wal;
- replica => Этот уровень поддерживает архивирование и репликацию wal;
- логический => Информация, необходимая для поддержки логического декодирования, добавляется на основе уровня реплики;
②fsync может реализовать гарантию безопасности данных, открыв принудительную синхронизацию
[Условия срабатывания]
- Вручную переключите журнал WAL. После выполнения pg_switch_xlog () WAL переключится на новый журнал, затем старый журнал WAL будет заархивирован;
- Запуск архивации после заполнения журнала WAL;
- Archive_timeout установлен. Если установлено archive_timeout = 60, то каждые 60 секунд будет срабатывать переключатель журнала WAL и одновременно запускаться архивирование журнала;
[Структура журнала]
запрос pg_waldump
pg_waldump -p -s [-n ]
※ Точку повтора можно получить с помощью pg_controldata.[Метод очистки]
- Автоматическая очистка
- При запуске базы данных и при реализации контрольных точек;
- Когда запускается процесс запуска, файлы XLOG перед текущей шкалой времени автоматически очищаются;
- Ручная очистка
- Проверьте, какие журналы WAL сохранились
※ Файл REDO WAL последней контрольной точки является последним журналом, и предыдущий можно удалить.
Производительность WAL
По умолчанию, каждый раз, когда транзакция фиксирует изменения, результат должен быть сброшен на диск. Для этого вначале страница сбрасывается из буферной памяти на дисковый кэш. А затем выполняется операция fsync для записи страницы на диск. То есть частые COMMIT приводят к частым fsync.
В PostgreSQL есть другой режим работы – асинхронный. При этом каждый COMMIT не приводит к fsync. Вместо этого страницы сбрасываются на диск по расписанию специальным процессом – WALWRITER. Этот процесс периодически просыпается и записывает на диск всё что изменилось за время пока он спал и опять засыпает.
В асинхронном режиме postgresql работает быстрее, но вы можете потерять некоторые данные при сбое.
Режим работы настраивается с помощью конфигурационного файла и настройки не требуют перезагрузки сервера. Это позволяет приложению устанавливать параметры на лету. Некоторые транзакции сразу запишут изменения на диск, то есть поработают в синхронном режиме. Другие транзакции будут работать в асинхронном режиме. Условно можно поделить операции на критичные и не критичные.
Следующие параметры отвечают за режим работы WAL:
- synchronous_commit – on/off – синхронный или асинхронный режим работы;
- wal_writer_delay = 200ms – период сброса на диск wal записей при асинхронном режиме.
Контрольная точка
При выполнении контрольной точки (CHECKPOINT) – принудительно сбрасываются на диск все грязные страницы, которые есть в буферном кэше. Это гарантирует что на момент контрольной точки все данные сбросились на диск и при восстановлении данных нужно читать не весь журнал WAL, а только ту часть которая была сделана после последней контрольной точки.
Сброс данных при контрольной точке не проходит моментально, это бы сильно нагрузило наш сервер. Чтобы не было пиковых нагрузок сброс идет примерно 50% времени от времени между контрольными точками. Например, контрольные точки делаются каждую минуту, тогда сброс осуществляют плавно в течении 30 секунд. Это регулируется и вы можете установить например 90% времени.
Контрольная точка также уменьшает размер необходимого дискового пространства. Так как весь журнал WAL не нужен, его можно периодически подрезать.
Отдельный серверный процесс контрольных точек автоматически выполняет контрольные точки с заданной частотой. Эту частоту можно настроить следующими параметрами:
- checkpoint_timeout – период выполнения контрольных точек в секундах;
- max_wal_size – максимальный размер, до которого может вырастать WAL во время автоматических контрольных точек;
- checkpoint_completion_target – время для завершения процедуры контрольной точки, как коэффициент для общего времени между контрольными точками. По умолчанию это значение равно 0.5.
Значения по умолчанию: 5 минут и 1 Гбайт, соответственно. Если после предыдущей контрольной точки новые записи WAL не добавились, следующие контрольные точки будут пропущены, даже если проходит время checkpoint_timeout. Также можно выполнить контрольную точку принудительно, воспользовавшись SQL-командой CHECKPOINT.
Уменьшение значений checkpoint_timeout и max_wal_size приводит к учащению контрольных точек. Но появляется дополнительная нагрузка на сервер.
Практика
В журнале WAL каждая запись имеет номер LSN (Log Sequence Number). С помощью функции pg_current_wal_lsn() можно посмотреть номер текущей записи:
Запомним номер текущей записи WAL в переменной “pos1”, затем создадим табличку с 1000 строк и опять сохраним текущий LSN в переменной “pos2”. Затем посмотрим разницу между этими числами, но в формате LSN, и получим число байт записанных в WAL при создании таблички с 1000 строками:
У нас вышло 138 KB! Так много получилось из за создания таблички, создание 1000 строк почти не нагрузит WAL.
Физически журнал хранится в файлах по 16 МБ в отдельном каталоге (PGDATA/pg_wal). На журнал можно взглянуть не только из файловой системы, но и с помощью функцию pg_ls_waldir():
Посмотреть на выполняемые процессы сервера postgres можно так:
К процессам, обслуживающим буферный кэш и журнал, можно отнести:
- checkpointer;
- background writer;
- walwriter;
Теперь давайте остановим сервер корректно и посмотрим в лог файл:
Как видим сервер просто открыл соединения на сокете и tcp порту 5432 и начал работу.
Теперь завершим работу сервера некорректно, используя опцию immediate:
Как видим журнал изменился! Перед тем, как начать принимать соединения, СУБД выполнила восстановление (automatic recovery in progress).
В прошлый раз мы познакомились с устройством одного из важных объектов разделяемой памяти, буферного кеша. Возможность потери информации из оперативной памяти — основная причина необходимости средств восстановления после сбоя. Сегодня мы поговорим про эти средства.
Увы, чудес не бывает: чтобы пережить потерю информации в оперативной памяти, все необходимое должно быть своевременно записано на диск (или другое энергонезависимое устройство).
Поэтому сделано вот что. Вместе с изменением данных ведется еще и журнал этих изменений. Когда мы что-то меняем на странице в буферном кеше, мы создаем в журнале запись об этом изменении. Запись содержит минимальную информацию, достаточную для того, чтобы при необходимости изменение можно было повторить.
Чтобы это работало, журнальная запись в обязательном порядке должна попасть на диск до того, как туда попадет измененная страница. Отсюда и название: журнал предзаписи (write-ahead log).
Если происходит сбой, данные на диске оказываются в рассогласованном состоянии: какие-то страницы были записаны раньше, какие-то — позже. Но остается и журнал, который можно прочитать и выполнить повторно те операции, которые уже были выполнены до сбоя, но результат которых не успел дойти до диска.
Почему принудительно не записывать на диск сами страницы с данными, зачем вместо этого выполнять двойную работу? Оказывается, так эффективнее.
Во-первых, журнал — это последовательный поток данных на запись. С последовательной записью даже HDD-диски справляются неплохо. А вот запись самих данных — случайная, потому что страницы разбросаны по диску более или менее хаотично.
Во-вторых, журнальная запись может быть гораздо меньше, чем страница.
В-третьих, при записи можно не заботиться о том, чтобы в каждый произвольный момент времени данные на диске оставались согласованными (такое требование сильно усложняет жизнь).
Ну и в-четвертых, как мы потом увидим, журнал (раз уж он есть) можно использовать не только для восстановления, но и для резервного копирования и репликации.Журналировать нужно все операции, при выполнении которых есть риск получить несогласованность на диске в случае сбоя. В частности, в журнал записываются следующие действия:
- изменение страниц в буферном кеше (как правило, это страницы таблиц и индексов) — так как измененная страница попадает на диск не сразу;
- фиксация и отмена транзакций — изменение статуса происходит в буферах XACT и тоже попадает на диск не сразу;
- файловые операции (создание и удаление файлов и каталогов, например, создание файлов при создании таблицы) — так как эти операции должны происходить синхронно с изменением данных.
- операции с нежурналируемыми (unlogged) таблицами — их название говорит само за себя;
- операции с временными таблицами — нет смысла, поскольку время жизни таких таблиц не превышает времени жизни создавшего их сеанса.
Логически журнал можно представить себе как последовательность записей различной длины. Каждая запись содержит данные о некоторой операции, предваренные стандартным заголовком. В заголовке, в числе прочего, указаны:
- номер транзакции, к которой относится запись;
- менеджер ресурсов — компонент системы, ответственный за запись;
- контрольная сумма (CRC) — позволяет определить повреждение данных;
- длина записи и ссылка на предыдущую запись.
На диске журнал хранится в виде файлов в каталоге $PGDATA/pg_wal. Каждый файл по умолчанию занимает 16 Мб. Размер можно увеличить, чтобы избежать большого числа файлов в одном каталоге. До версии PostgreSQL 11 это можно было сделать только при компиляции исходных кодов, но теперь размер можно указать при инициализации кластера (ключ --wal-segsize ).
Журнальные записи попадают в текущий использующийся файл; когда он заканчивается — начинает использоваться следующий.
В разделяемой памяти сервера для журнала выделены специальные буферы. Размер журнального кеша задается параметром wal_buffers (значение по умолчанию подразумевает автоматическую настройку: выделяется 1/32 часть буферного кеша).
Журнальный кеш устроен наподобие буферного кеша, но работает преимущественно в режиме кольцевого буфера: записи добавляются в «голову», а записываются на диск с «хвоста».
Позиции записи («хвоста») и вставки («головы») показывают функции pg_current_wal_lsn и pg_current_wal_insert_lsn соответственно:
Для того, чтобы сослаться на определенную запись, используется тип данных pg_lsn (LSN = log sequence number) — это 64-битное число, представляющее собой байтовое смещение до записи относительно начала журнала. LSN выводится как два 32-битных числа в шестнадцатеричной системе через косую черту.Можно узнать, в каком файле мы найдем нужную позицию, и с каким смещением от начала файла:
Имя файла состоит из двух частей. Старшие 8 шестнадцатеричных разрядов показывают номер ветви времени (она используется при восстановлении из резервной копии), остаток соответствует старшим разрядам LSN (а оставшиеся младшие разряды LSN показывают смещение).Журнальные файлы можно посмотреть в файловой системе в каталоге $PGDATA/pg_wal/, но начиная с PostgreSQL 10 их также можно увидеть специальной функцией:
Посмотрим, как происходит журналирование и как обеспечивается упреждающая запись. Создадим таблицу:
Мы будем заглядывать в заголовок табличной страницы. Для этого нам понадобится уже хорошо знакомое расширение:
Начнем транзакцию и запомним позицию вставки в журнал:
Теперь выполним какую-нибудь операцию, например, обновим строку:
Это изменение было записано и в журнал, позиция вставки изменилась:
Чтобы гарантировать, что измененная страница данных не будет вытеснена на диск раньше, чем журнальная запись, в заголовке страницы сохраняется номер LSN последней журнальной записи, относящейся к этой странице:
Надо учитывать, что журнал общий для всего кластера, и в него все время попадают новые записи. Поэтому LSN на странице может оказаться меньше, чем значение, которое только что вернула функция pg_current_wal_insert_lsn. Но в нашей системе ничего не происходит, поэтому цифры совпадают.Теперь завершим транзакцию.
Запись о фиксации также попадает в журнал, и позиция снова меняется:
Фиксация меняет статус транзакции в структуре, называемой XACT (мы уже говорили про нее). Статусы хранятся в файлах, но для них тоже используется свой кеш, который занимает в разделяемой памяти 128 страниц. Поэтому и для страниц XACT приходится отслеживать номер LSN последней журнальной записи. Но эта информация хранится не в самой странице, а в оперативной памяти.В какой-то момент созданные журнальные записи будут записаны на диск. В какой именно — мы поговорим в другой раз, но в нашем случае это уже произошло:
После этого момента страницы данных и XACT могут быть вытеснены из кеша. А вот если бы потребовалось вытеснить их раньше, это было бы обнаружено и журнальные записи были бы принудительно записаны первыми.Зная две позиции LSN, можно получить размер журнальных записей между ними (в байтах) простым вычитанием одной позиции из другой. Надо только привести позиции к типу pg_lsn:
В данном случае обновление строки и фиксация потребовали 108 байтов в журнале.Таким же способом можно оценить, какой объем журнальных записей генерируется сервером за единицу времени при определенной нагрузке. Это важная информация, которая потребуется при настройке (о чем мы поговорим в следующий раз).
Теперь воспользуемся утилитой pg_waldump, чтобы посмотреть на созданные журнальные записи.
Утилита может работать и с диапазоном LSN (как в этом примере), и выбрать записи для указанной транзакции. Запускать ее следует от имени пользователя ОС postgres, так как ей требуется доступ к журнальным файлам на диске.
Здесь мы видим заголовки двух записей.Первая — операция HOT_UPDATE, относящаяся к менеджеру ресурсов Heap. Имя файла и номер страницы указаны в поле blkref и совпадают с обновленной табличной страницей:
Вторая запись — COMMIT, относящаяся к менеджеру ресурсов Transaction.Не самый удобочитаемый формат, но при необходимости разобраться можно.
Когда мы стартуем сервер, первым делом запускается процесс postmaster, а он, в свою очередь, запускает процесс startup, задача которого — обеспечить восстановление, если произошел сбой.
Чтобы определить, требуется ли восстановление, startup заглядывает в специальный управляющий файл $PGDATA/global/pg_control и смотрит на статус кластера. Мы можем и сами проверить статус с помощью утилиты pg_controldata:
У аккуратно остановленного сервера статус будет «shut down». Если сервер не работает, а статус остался «in production», это означает, что СУБД упала и тогда автоматически будет выполнено восстановление.Для восстановления процесс startup будет последовательно читать журнал и применять записи к страницам, если в этом есть необходимость. Необходимость можно проверить, сравнив LSN страницы на диске с LSN журнальной записи. Если LSN страницы оказался больше, то запись применять не нужно. А на самом деле — даже и нельзя, потому что записи рассчитаны на строго последовательное применение.
Есть исключения. Часть записей формируются как полный образ страницы (FPI, full page image), и понятно, что такой образ можно применить к странице в любом состоянии — он все равно сотрет все, что там было. Еще изменение статуса транзакции можно применять к любой версии страницы XACT — поэтому внутри таких страниц нет необходимости хранить LSN.
Изменение страниц при восстановлении происходит в буферном кеше, как при обычной работе — для этого postmaster запускает необходимые фоновые процессы.
Аналогично журнальные записи применяются и к файлам: например, если запись говорит о том, что файл должен существовать, а его нет — файл создается.
Ну и в самом конце процесса восстановления все нежурналируемые таблицы перезаписываются «пустышками» из своих init-слоев.
Это сильно упрощенное изложение алгоритма. В частности, мы пока ничего не говорили о том, с какого места надо начинать чтение журнальных записей (этот разговор придется отложить до рассмотрения контрольной точки).
И последнее уточнение. «По классике» процесс восстановления состоит из двух фаз. На первой фазе (roll forward) накатываются журнальные записи, при этом сервер повторяет всю потерянную при сбое работу. На второй (roll back) — откатываются транзакции, которые не были зафиксированы на момент сбоя. Но PostgreSQL не нуждается во второй фазе. Как мы рассматривали ранее, благодаря особенностям реализации многоверсионности транзакции не надо откатывать физически; достаточно того, что в XACT не будет установлен бит фиксации.
Разберем процесс репликации. Репликация в PostgreSQL – это процесс синхронизации нескольких копий кластера баз данных на разных серверах. Она бывает логической и физической.
Логическая репликация
Поменяем параметр wal_level = logical для первого кластера и перезапустим этот его:
В первом кластере создадим публикацию (CREATE PUBLICATION) и посмотрим на неё с помощью команды \dRp+:
Во втором кластере подписываемся на эту публикацию (CREATE SUBSCRIPTION) и отключаем первоначальное копирование данных (copy_data = false):
Выше вы можете заметить предупреждение, что на публикующем сервере создался слот репликации (NOTICE: created replication slot “test_sub” on publisher)!
Теперь посмотрим на подписку с помощью команды \dRs:
Проверим настроенную логическую репликацию. Для этого в первом кластере, вставим новую строчку в опубликованной табличке:
Во втором кластере увидим эту строку:
Состояние подписки можно посмотреть в представлении pg_stat_subscription:
К процессам сервера подписчика добавился logical replication worker:
Если репликация больше не нужна, надо удалить подписку, иначе на публикующем сервере останется открытым слот репликации:
На этом этот курс подошёл к концу! За основу взяты видео с этого плейлиста на YouTube. Я просто проделал все на PostgreSQL 13 и лишь некоторые моменты связанные с репликацией отличаются от PostgreSQL 10. В дальнейшем, если вернусь к теме PostgreSQL постараюсь усовершенствовать этот курс, спасибо за внимание!
wal_level determines how much information is written to the WAL. The default value is replica , which writes enough data to support WAL archiving and replication, including running read-only queries on a standby server. minimal removes all logging except the information required to recover from a crash or immediate shutdown. Finally, logical adds information necessary to support logical decoding. Each level includes the information logged at all lower levels. This parameter can only be set at server start.
In minimal level, no information is logged for permanent relations for the remainder of a transaction that creates or rewrites them. This can make operations much faster (see Section 14.4.7). Operations that initiate this optimization include:
ALTER . SET TABLESPACE CLUSTER CREATE TABLE REFRESH MATERIALIZED VIEW (without CONCURRENTLY ) REINDEX TRUNCATE But minimal WAL does not contain enough information to reconstruct the data from a base backup and the WAL logs, so replica or higher must be used to enable WAL archiving (archive_mode) and streaming replication. Note that changing wal_level to minimal makes any base backups taken before unavailable for archive recovery and standby server, which may lead to data loss.
In logical level, the same information is logged as with replica , plus information needed to allow extracting logical change sets from the WAL. Using a level of logical will increase the WAL volume, particularly if many tables are configured for REPLICA IDENTITY FULL and many UPDATE and DELETE statements are executed.
In releases prior to 9.6, this parameter also allowed the values archive and hot_standby . These are still accepted but mapped to replica .
If this parameter is on, the PostgreSQL server will try to make sure that updates are physically written to disk, by issuing fsync() system calls or various equivalent methods (see wal_sync_method). This ensures that the database cluster can recover to a consistent state after an operating system or hardware crash.
While turning off fsync is often a performance benefit, this can result in unrecoverable data corruption in the event of a power failure or system crash. Thus it is only advisable to turn off fsync if you can easily recreate your entire database from external data.
Examples of safe circumstances for turning off fsync include the initial loading of a new database cluster from a backup file, using a database cluster for processing a batch of data after which the database will be thrown away and recreated, or for a read-only database clone which gets recreated frequently and is not used for failover. High quality hardware alone is not a sufficient justification for turning off fsync .
For reliable recovery when changing fsync off to on, it is necessary to force all modified buffers in the kernel to durable storage. This can be done while the cluster is shutdown or while fsync is on by running initdb --sync-only , running sync , unmounting the file system, or rebooting the server.
In many situations, turning off synchronous_commit for noncritical transactions can provide much of the potential performance benefit of turning off fsync , without the attendant risks of data corruption.
fsync can only be set in the postgresql.conf file or on the server command line. If you turn this parameter off, also consider turning off full_page_writes.
Specifies how much WAL processing must complete before the database server returns a “ success ” indication to the client. Valid values are remote_apply , on (the default), remote_write , local , and off .
If synchronous_standby_names is empty, the only meaningful settings are on and off ; remote_apply , remote_write and local all provide the same local synchronization level as on . The local behavior of all non- off modes is to wait for local flush of WAL to disk. In off mode, there is no waiting, so there can be a delay between when success is reported to the client and when the transaction is later guaranteed to be safe against a server crash. (The maximum delay is three times wal_writer_delay.) Unlike fsync, setting this parameter to off does not create any risk of database inconsistency: an operating system or database crash might result in some recent allegedly-committed transactions being lost, but the database state will be just the same as if those transactions had been aborted cleanly. So, turning synchronous_commit off can be a useful alternative when performance is more important than exact certainty about the durability of a transaction. For more discussion see Section 30.4.
If synchronous_standby_names is non-empty, synchronous_commit also controls whether transaction commits will wait for their WAL records to be processed on the standby server(s).
When set to remote_apply , commits will wait until replies from the current synchronous standby(s) indicate they have received the commit record of the transaction and applied it, so that it has become visible to queries on the standby(s), and also written to durable storage on the standbys. This will cause much larger commit delays than previous settings since it waits for WAL replay. When set to on , commits wait until replies from the current synchronous standby(s) indicate they have received the commit record of the transaction and flushed it to durable storage. This ensures the transaction will not be lost unless both the primary and all synchronous standbys suffer corruption of their database storage. When set to remote_write , commits will wait until replies from the current synchronous standby(s) indicate they have received the commit record of the transaction and written it to their file systems. This setting ensures data preservation if a standby instance of PostgreSQL crashes, but not if the standby suffers an operating-system-level crash because the data has not necessarily reached durable storage on the standby. The setting local causes commits to wait for local flush to disk, but not for replication. This is usually not desirable when synchronous replication is in use, but is provided for completeness.
This parameter can be changed at any time; the behavior for any one transaction is determined by the setting in effect when it commits. It is therefore possible, and useful, to have some transactions commit synchronously and others asynchronously. For example, to make a single multistatement transaction commit asynchronously when the default is the opposite, issue SET LOCAL synchronous_commit TO OFF within the transaction.
Table 20.1 summarizes the capabilities of the synchronous_commit settings.
Table 20.1. synchronous_commit Modes
synchronous_commit setting local durable commit standby durable commit after PG crash standby durable commit after OS crash standby query consistency remote_apply • • • • on • • • remote_write • • local • off Физическая репликация PostgreSQL
Алгоритм создания такой репликации следующий:
- Делаем резервную копию с помощью pg_basebackup. С помощью отдельных опций указываем что копию нужно подготовить к дальнейшей репликации.
- Разворачиваем полученную резервную копию на сервере репликации.
- Там же, создаем специальный файл с настройками репликации (если были указаны нужные опции для команды pg_basebackup, то все необходимые файлы, нужные для репликации, создадутся автоматом):
- в 10 версии PostgreSQL создаём файл recovery.conf, прописываем там standby_mode = on;
- начиная с 12 версии создаём пустой файл standby.signal;
Сервер репликации может принимать запросы, но только на чтение. Этот сервер не генерирует wal записи, а продолжает их получать из потока по протоколу репликации с основного сервера.
Журнальные записи можно передавать по протоколу репликации или можно использовать архив WAL. Но обычно применяют первое.
На главном сервере появляется процесс wal sender, который передаёт wal записи. На сервере репликации появляется wal receiver, который принимает эти записи.
Репликация может быть синхронной и асинхронной. При синхронной репликации основной сервер не только отправляет запись на реплику, но и дожидается подтверждения что запись туда дошла и была там применена. Но синхронная реплика сильно тормозит производительность кластера.
Логическая репликация PostgreSQL
При репликации передаются wal записи, но для работы логической репликации нужно изменить формат этих записей. Для этого нужно поменять параметр кластера wal_level = logical.
Поставщик – передаёт логические wal записи. Но передаются не все команды, а только INSERT, UPDATE, DELETE и TRANSCATE. То есть CREATE не передаётся, поэтому нужна начальная физическая синхронизация. Также мы можем ограничить публикацию некоторыми командами, например передавать только INSERT, а UPDATE не передавать.
При такой репликации всегда используется слот логической репликации. Благодаря которому понятно, какие записи уже были переданы, а какие нет.
Подписчик – получает wal записи и применяет изменения без разбора, трансформаций и планирования.
Подписчик и поставщик это равноценные сервера, они оба доступны на чтение и запись для клиентов. Поэтому могут возникать конфликты. Например на поставщике выполняется INSERT, а на подписчике уже такая строка есть. Подобные конфликты решаются пока только в ручном режиме.
На поставщике работает уже знакомый процесс wal sender, а на подписчике logical replication worker который получает логические wal записи и применяет их от имени суперпользователя.
Переключение на реплику
Переключение на реплику осуществляется либо планово, либо аварийно. Но в любом случае оно происходит в ручном режиме. Хотя процесс можно автоматизировать, например с помощью скриптов.
Для восстановления основного сервера его обычно превращают в реплику и синхронизируют с бывшей репликой. После синхронизации сервера опять меняют местами.
Восстановление основного сервера проделывают следующими способами:
- Если основной сервер был остановлен корректно и все журнальные записи успели перейти на реплику, то можно просто поменять сервера местами:
- останавливаем основной сервер;
- реплику превращаем в основной сервер;
- основной сервер превращаем в реплику.
- реплику превращаем в основной сервер (пусть клиенты пока поработают на этом сервере);
- на основном сервере (который был выключен некорректно) очищаем каталог PGDATA;
- затем основной сервер превращаем в реплику.
Сценарии использования логической репликации
- Собираем данные на центральном кластере. Например в каждом филиале есть сервер баз данных и данные со всех этих серверов собираются на каком-нибудь центральном сервере.
- Распространяем данные с центрального кластера. Например на центральном сервере мы меняем какую-то табличку, а подписчики в филиалах обновляют её у себя.
- Можно использовать логическую репликация для обновления кластера. Так как версия Postgres не имеет значение, можем выключить и обновить подписчик до новой версии. Затем поменять местами поставщика и подписчика. И наконец выключить и обновить основной сервер.
- Мультимастер – кластер в котором данные могут менять несколько серверов. Логическая репликация для этого тоже предназначена, но в PostgreSQL пока реализовать такое нельзя. Существует проблема зацикливания. Если оба сервера будут публиковать одну и туже таблицу и будут подписчиками друг на друга, то возникнет зацикливание и произойдёт ошибка репликации.
Читайте также: