47.2. Концепции логического декодирования #
47.2.1. Логическое декодирование #
Логическое декодирование — это процедура извлечения всех постоянных изменений, происходящих в таблицах базы данных, в согласованном и понятном формате, который можно интерпретировать, не имея полного представления о внутреннем состоянии базы данных.
В PostgreSQL логическое декодирование реализуется путём перевода содержимого журнала предзаписи, описывающего изменения на уровне хранения, в специальную форму уровня приложения, например, в поток кортежей или операторов SQL.
47.2.2. Слоты репликации #
В контексте логической репликации слот представляет поток изменений, которые могут быть воспроизведены клиентом в том порядке, в каком они происходили на исходном сервере. Через каждый слот передаётся последовательность изменений в одной базе данных.
Примечание
В PostgreSQL также есть слоты потоковой репликации (см. Подраздел 26.2.5), но они используются несколько по-другому.
Слоту репликации назначается идентификатор, уникальный для всех баз данных в кластере PostgreSQL. Слоты сохраняются независимо от подключений, использующих их, и защищены от сбоев сервера.
При обычных условиях через логический слот каждое изменение передаётся только один раз. Текущая позиция в каждом слоте сохраняется только в контрольной точке, так что в случае сбоя слот мог вернуться к предыдущему LSN, вследствие чего последние изменения могут быть переданы повторно при перезапуске сервера. За исключение нежелательных эффектов от повторной обработки одного и того же сообщения отвечают клиенты логического декодирования. Клиенты могут запоминать при декодировании, какой последний LSN они уже получали, и пропускать повторяющиеся данные или (при использовании протокола репликации) запрашивать, чтобы декодирование начиналось с этого LSN, а не с позиции, выбираемой сервером. Для этого разработан механизм отслеживания репликации, о котором можно узнать подробнее в описании источников репликации.
Для одной базы данных могут существовать несколько независимых слотов. Каждый слот имеет собственное состояние, что позволяет различным потребителям получать изменения с разных позиций в потоке изменений базы данных. Для большинства приложений каждому потребителю требуется отдельный слот.
Слот логической репликации ничего не знает о состоянии получателя(ей). Возможно даже иметь несколько различных потребителей одного слота в разные моменты времени; они просто будут получать изменения с момента, когда их перестал получать предыдущий потребитель. Но в любой определённый момент получать изменения может только один потребитель.
Логический слот репликации также можно создать на сервере горячего резерва. Чтобы запретить VACUUM
удалять требуемые строки из системных каталогов, на резервном сервере необходимо установить параметр hot_standby_feedback
. Несмотря на это, если какие-либо требуемые строки будут удалены, слот станет аннулирован. Настоятельно рекомендуется использовать физический слот между ведущим и резервным сервером. В противном случае hot_standby_feedback
будет работать только пока соединение активно (например, перезапуск узла может разорвать соединение). Затем ведущий сервер может удалить строки системного каталога, которые могут потребоваться для логического декодирования на резервном сервере (поскольку ведущий сервер не знает о catalog_xmin
на резервном сервере). Существующие логические слоты в режиме ожидания также становятся недействительными, если значение wal_level
на основном сервере становится ниже logical
. Это происходит, как только резервный сервер обнаруживает такое изменение в потоке WAL и означает, что для отстающих процессов walsender (если таковые имеются) некоторые записи WAL до изменения параметра wal_level
на ведущем сервере не будут декодированы.
Для создания логического слота необходима информация обо всех текущих транзакциях. На ведущем сервере эта информация доступна напрямую, а на резервном её необходимо получать от ведущего. Таким образом, при создании слота может потребоваться ожидание каких-либо действий на ведущем сервере. Если ведущий сервер простаивает, создание логического слота на резервном может занять много времени. Это время можно сократить, вызвав функцию pg_log_standby_snapshot
на ведущем сервере.
Внимание
Слоты репликации сохраняются при сбоях сервера и ничего не знают о состоянии их потребителя. Они не дают удалять требуемые ресурсы, даже когда не используются никаким подключением. На это уходит место в хранилище, так как ни сегменты WAL, ни требуемые строки из системных каталогов нельзя будет удалить в результате VACUUM
, пока они нужны этому слоту репликации. В особых случаях это может привести к отключению базы для предотвращения зацикливания идентификаторов транзакций (см. Подраздел 24.1.5). Поэтому, если слот больше не требуется, его следует ликвидировать.
47.2.3. Синхронизация слотов репликации #
Слоты логической репликации на главном сервере можно синхронизировать с горячим резервом следующим образом: сначала нужно создать слот, либо вызвав функцию pg_create_logical_replication_slot
с параметром failover
, либо указав параметр failover
в команде CREATE SUBSCRIPTION
. Кроме того, необходимо вызвать на резервном сервере функцию sync_replication_slots
. Если задать на резервном сервере sync_replication_slots
, слоты отработки отказа будут периодически синхронизироваться рабочим процессом slotsync. Чтобы синхронизация работала корректно, необходим слот физической репликации между главным и резервным сервером (например, primary_slot_name
необходимо настроить на резервном), а также необходимо включить на резервном hot_standby_feedback
. Также необходимо указать действительное значение dbname
в primary_conninfo
. Крайне рекомендуется внести слот физической репликации в список synchronized_standby_slots
на главном сервере, чтобы подписки не получали изменения раньше горячего резерва. Даже с правильной настройкой будет заметна некоторая задержка при отправке изменений логическим подписчикам из-за ожидания слотов, перечисленных в списке synchronized_standby_slots
. При использовании synchronized_standby_slots
главный сервер не отключится полностью, пока резервные серверы, связанные с перечисленными там слотами, не подтвердят получение последних обновлений WAL.
Примечание
Хотя sync_replication_slots
включает автоматическую периодическую синхронизацию слотов аварийного переключения, их также можно синхронизировать вручную на резервном сервере с помощью функции pg_sync_replication_slots
. Однако данная функциональность в первую очередь разработана в целях тестирования и отладки, а потому должна применяться с осторожностью. В отличие от автоматической синхронизации, она не включает в себя циклические повторные попытки, что с большей вероятностью приведёт к ошибкам, в особенности во время начальной синхронизации, при которой необходимые файлы WAL или строки каталога для слота уже могли быть перемещены или вскоре будут перемещены с резервного сервера. И, напротив, автоматическая синхронизация через sync_replication_slots
позволяет постоянно обновлять слоты, что обеспечивает бесшовное переключение и отказоустойчивость, а потому является предпочтительным способом синхронизации слотов.
При рекомендованной конфигурации синхронизации слотов, когда первоначальная синхронизация выполняется автоматически или вручную через pg_sync_replication_slot
, на резервном сервере синхронизированный слот будет поддерживаться только при следующих условиях: слот логической репликации на ведущем сервере должен сохранять WAL и строки системного каталога, которые всё ещё доступны на резервном. Это обеспечит целостность данных и бесперебойную работу логической репликации после переключения. Если с резервного сервера уже были удалены необходимые WAL или строки системного каталога, слот не будет поддерживаться во избежание потери данных. В таких случаях в журнале появится следующее сообщение:
LOG: could not synchronize replication slot "failover_slot" (не удалось синхронизировать слот репликации "failover_slot") DETAIL: Synchronization could lead to data loss, because the remote slot needs WAL at LSN 0/3003F28 and catalog xmin 754, but the standby has LSN 0/3003F28 and catalog xmin 756 (Синхронизация может привести к потере данных, поскольку удалённый слот запрашивает запись WAL от LSN 0/3003F28 и каталог xmin 754, но у резервного сервера уже есть запись WAL от LSN 0/3003F28 и каталог xmin 754).
Если слот логической репликации активно используется потребителем, ручное вмешательство не требуется, поскольку слот автоматически продвинется дальше, а синхронизация возобновится в ходе следующего цикла. Однако при отсутствии настроенного потребителя рекомендуется вручную продвинуть слот на ведущий сервер через pg_logical_slot_get_changes
или pg_logical_slot_get_binary_changes
, что продолжит синхронизацию.
Возможность возобновить процесс логической репликации после отказа зависит от значения pg_replication_slots.synced
для слотов, синхронизированных на резервном сервере на момент отработки отказа. Для логической репликации после отказа могут использоваться только постоянные слоты, которые были синхронизированы на резервном сервере до отказа. Временные синхронизированные слоты для этого использовать нельзя, то есть возобновить логическую репликацию для таких слотов невозможно. Например, если синхронизированный слот не может стать постоянным на резервном из-за того, что подписка отключена, то подписка не будет возобновлена после отказа, даже если её включить.
Чтобы возобновить логическую репликацию из синхронизированных логических слотов после отработки отказа, необходимо в строке conninfo подписки указать главный сервер. Это можно сделать командой ALTER SUBSCRIPTION ... CONNECTION
. Рекомендуется отключить подписки до повышения резерва и включить после изменения строки подключения.
Внимание
Всегда есть вероятность, что старый главный сервер снова включится во время повышения, и если подписки включены, то подписчики логической репликации могут продолжить получать данные с прошлого главного сервера после повышения резерва и вплоть до момента, пока не изменится строка подключения. Это может вызвать конфликты в данных, и подписчики не смогут продолжить репликацию с нового главного сервера.
47.2.4. Модули вывода #
Модули вывода переводят данные из внутреннего представления в журнале предзаписи в формат, устраивающий потребителя слота репликации.
47.2.5. Экспортированные снимки #
Когда новый слот репликации создаётся через интерфейс потоковой репликации, экспортируется снимок (см. CREATE_REPLICATION_SLOT), который будет показывать ровно то состояние базы данных, изменения после которого будут включаться в поток изменений. Используя его, можно создать новую реплику, воспользовавшись командой SET TRANSACTION SNAPSHOT
, чтобы получить состояние базы в момент создания слота. После этого данную транзакцию можно использовать для выгрузки состояния базы на момент экспорта снимка, а затем изменять это состояние, применяя содержимое слота, так что никакие изменения не будут потеряны.
Приложения, которым не требуется экспорт снимка, могут подавить его, воспользовавшись указанием SNAPSHOT 'nothing'
.