Перейти к основному содержимому

Непрерывное архивирование и восстановление до заданной точки во времени (PITR)

примечание

Эта страница переведена при помощи нейросети GigaChat.

Во время работы PostgreSQL постоянно поддерживает журнал предварительной записи (WAL) в подкаталоге pg_wal/ каталога данных кластера. Журнал регистрирует каждое изменение, внесенное в файлы данных базы данных. Этот журнал существует прежде всего для обеспечения безопасности при сбоях: если система выходит из строя, база данных может быть восстановлена до согласованного состояния путем «воспроизведения» записей журнала, сделанных с момента последней контрольной точки. Однако наличие журнала делает возможным использование третьей стратегии резервного копирования баз данных: можно объединить резервное копирование на уровне файловой системы с резервным копированием файлов WAL. Если требуется восстановление, то сначала восстанавливается резервная копия файловой системы, а затем воспроизводятся сохраненные файлы WAL, чтобы привести систему в актуальное состояние. Этот подход сложнее в администрировании, чем любой из предыдущих подходов, но он имеет некоторые значительные преимущества:

  • Не нужна идеально согласованная резервная копия файловой системы в качестве отправной точки. Любая внутренняя несогласованность в резервной копии будет исправлена воспроизведением журнала (это не сильно отличается от того, что происходит во время восстановления после сбоя). Поэтому становится не нужным средство моментального снимка файловой системы, достаточно использовать tar или аналогичный инструмент архивации.
  • Поскольку можно комбинировать бесконечно длинную последовательность файлов WAL для воспроизведения, непрерывного резервного копирования можно достичь просто продолжая архивировать файлы WAL. Это особенно ценно для больших баз данных, где может быть неудобно часто делать полную резервную копию.
  • Не обязательно воспроизводить записи WAL до самого конца. Можно было бы остановить воспроизведение в любой точке и получить согласованный снимок базы данных на тот момент времени. Таким образом, эта техника поддерживает восстановление на определенный момент времени: возможно восстановить базу данных до ее состояния в любое время с момента создания резервной копии.
  • Если непрерывно подается серия файлов WAL на другую машину, загруженную тем же файлом базовой резервной копии, то получается система горячего резерва: в любой момент можно запустить вторую машину, и она будет иметь почти актуальную копию базы данных.
Примечание

pg_dump и pg_dumpall не создают резервные копии на уровне файловой системы и не могут использоваться в рамках решения для непрерывного архивирования. Такие дампы являются логическими и не содержат достаточно информации, чтобы их можно было использовать при воспроизведении WAL.

Как и при использовании метода резервного копирования файловой системы, этот метод может поддерживать восстановление всего кластера баз данных, а не его подмножества. Кроме того, он требует большого объема архивного хранилища: базовая резервная копия может быть объемной, а активная система будет генерировать много мегабайт трафика WAL, которые должны быть заархивированы. Тем не менее, это предпочтительный метод резервного копирования во многих ситуациях, когда требуется высокая надежность.

Чтобы успешно восстановить с помощью непрерывного архивирования (также называемым «оперативным резервным копированием» многими поставщиками баз данных), нужна непрерывная последовательность архивированных файлов WAL, которая простирается назад, по крайней мере, до начала резервного копирования. Таким образом, чтобы начать работу, необходимо настроить и протестировать свою процедуру архивации файлов WAL до создания первой базовой резервной копии. Соответственно, сначала обсудим механизм архивации файлов WAL.

Настройка архивирования WAL

В абстрактном смысле работающая система PostgreSQL производит бесконечно длинную последовательность записей WAL. Система физически делит эту последовательность на файлы сегментов WAL, которые обычно имеют размер 16 МБ каждый (хотя размер сегмента можно изменить во время initdb). Файлам сегментов даны числовые имена, отражающие их положение в абстрактной последовательности WAL. Когда не используется архивирование WAL, система обычно создает лишь несколько файлов сегментов, а затем «перерабатывает» их, переименовывая ненужные больше файлы сегментов в более высокие номера сегментов. Предполагается, что файлы сегментов, содержимое которых предшествует последней контрольной точке, больше не представляют интереса и могут быть переработаны.

При архивировании данных WAL необходимо захватывать содержимое каждого сегментного файла после его заполнения и сохранять эти данные где-то до того, как сегментный файл будет переработан для повторного использования. В зависимости от приложения и доступного оборудования может быть множество различных способов «сохранения данных где-то»: можно скопировать сегментные файлы в каталог, смонтированный через NFS, на другой машине, записать их на ленточный накопитель (убедившись, что есть способ идентификации исходного имени каждого файла) или объединить их вместе и записать на компакт-диски или сделать что-то совершенно другое. Чтобы предоставить администратору базы данных гибкость, PostgreSQL пытается не делать никаких предположений о том, как будет выполняться архивирование. Вместо этого PostgreSQL позволяет администратору указать команду оболочки или библиотеку архива, которая должна быть выполнена для копирования завершенного сегментного файла туда, куда он должен идти. Это может быть так же просто, как команда оболочки, использующая cp, или она может вызывать сложную функцию на языке C – все зависит от желания администратора БД.

Чтобы включить архивирование WAL, установите параметр конфигурации wal_level на значение replica или выше, archive_mode на on, укажите команду оболочки, которую следует использовать в параметре конфигурации archive_command или укажите библиотеку, которую следует использовать в параметре конфигурации archive_library. На практике эти настройки всегда будут размещены в файле postgresql.conf.

В archive_command, %p заменяется путем к файлу, который нужно заархивировать, а %f заменяется только именем файла (путь является относительным к текущему рабочему каталогу, т.е. каталогу данных кластера). Используйте %%, если нужно встроить фактический символ % в команду. Самая простая полезная команда выглядит примерно так:

archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'  # Unix
archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"' # Windows

Эта команда будет копировать сегменты WAL, доступные для архивации, в каталог /mnt/server/archivedir (это пример, а не рекомендация, он может не работать на всех платформах). После замены параметров %p и %f фактически выполняемая команда может выглядеть так:

test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_wal/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065

Аналогичная команда будет создана для каждого нового файла, который нужно заархивировать.

Команда архивирования будет выполнена под управлением того же пользователя, что и сервер PostgreSQL. Поскольку архивируемые последовательности файлов WAL фактически содержат все, что есть в базе данных, то нужно будет защитить архивируемые данные от посторонних глаз, например, сохраните архив в каталог, чтение которого запрещено для группы и остальных пользователей.

Важно, чтобы команда архивации возвращала нулевой статус выхода только в том случае, если она успешна. Получив нулевой результат, PostgreSQL предполагает, что файл был успешно заархивирован, и удаляет его или повторно использует. Однако ненулевой статус сообщает PostgreSQL, что файл не был заархивирован, он будет пытаться снова периодически до тех пор, пока это не удастся.

Другой способ архивации – использовать специальный модуль архивации в качестве archive_library. Поскольку такие модули написаны на C, создание собственного модуля может потребовать значительно больших усилий, чем написание команды оболочки. Однако модули архивирования могут быть более производительными, чем архивирование через оболочку, и они будут иметь доступ ко многим полезным ресурсам сервера.

Когда команда архивирования завершается сигналом (кроме SIGTERM, который используется в рамках завершения работы сервера) или ошибкой оболочки с кодом выхода, превышающим 125 (например, команда не найдена), или если функция архивирования выдает ERROR или FATAL, процесс архиватора прерывается и перезапускается управляющим процессом postmaster. В таких случаях сбой не регистрируется в pg_stat_archiver.

Команды архивирования и библиотеки должны быть в целом разработаны таким образом, чтобы они отказывались перезаписывать любой ранее существующий файл архива. Это важная функция безопасности для сохранения целостности архива в случае ошибки администратора (например, при отправке вывода двух разных серверов в один и тот же каталог архива).

В редких случаях PostgreSQL может попытаться повторно архивировать ранее заархивированный файл WAL. Например, если система выходит из строя до того, как сервер делает надежную запись об успешном архивировании, он пытается снова заархивировать файл после перезапуска (при условии, что архивирование все еще включено). Когда команда или библиотека архивирования обнаруживает существовавший файл, первая должна возвращать нулевой статус, а вторая — true, если содержимое файла WAL полностью совпадает с содержимым существующего архива, который находится в хранилище. Если содержимое существующего файла отличается от содержимого архивируемого файла WAL, команда или библиотека архивирования должны возвращать ненулевой статус или false соответственно.

Рекомендуется протестировать предложенную команду или библиотеку архивации, чтобы убедиться, что она действительно не перезаписывает существующий файл, а возвращает ненулевой статус или false, соответственно, в этом случае. Пример команды выше для Unix обеспечивает это, включая отдельный шаг test. На некоторых платформах Unix cp имеет переключатели, такие как -i, которые могут использоваться для выполнения той же задачи менее многословно, но не нужно полагаться на них без проверки того, что возвращается правильный код завершения (в частности, GNU cp вернет состояние нуля, когда используется -i и целевой файл уже существует, что является нежелательным поведением).

При проектировании системы архивирования учитывайте, что произойдет, если команда архивирования или библиотека будут продолжать терпеть неудачу из-за какого-либо аспекта, требующего вмешательства оператора, или если архив закончится. Например, это может произойти, если запись производится на ленту без авточейнджера. Когда лента заполняется, ничего больше нельзя архивировать, пока лента не будет заменена. Необходимо убедиться, что любое условие ошибки или запрос к оператору-человеку сообщается соответствующим образом, чтобы ситуация могла быть разрешена достаточно быстро. Каталог pg_wal/ будет продолжать заполняться сегментными файлами WAL до тех пор, пока ситуация не разрешится (если файловая система, содержащая pg_wal/, переполнится, PostgreSQL выполнит аварийное завершение работы. Никакие подтвержденные транзакции не будут потеряны, но база данных останется офлайн до тех пор, пока не освободится некоторое пространство).

Скорость команды архивирования или библиотеки не важна, пока она может идти в ногу со средней скоростью, с которой сервер генерирует данные WAL. Нормальная работа продолжается даже в том случае, если процесс архивирования немного отстает. Если архивация значительно отстает, это увеличит объем данных, которые были бы потеряны в случае катастрофы. Это также означает, что каталог pg_wal/ будет содержать большое количество сегментных файлов, которые еще не были заархивированы, что в конечном итоге может превысить доступное дисковое пространство. Рекомендуется контролировать процесс архивирования, чтобы убедиться, что он работает так, как требуется.

При написании команды архивации или библиотеки нужно исходить из предположения, что имена файлов, которые будут заархивированы, могут быть длиной до 64 символов и могут содержать любую комбинацию букв ASCII, цифр и точек. Не обязательно сохранять исходный относительный путь (%p), но необходимо сохранить имя файла (%f).

Обратите внимание, что хотя архивация WAL позволит восстановить любые изменения, внесенные в данные базы данных PostgreSQL, она не восстановит изменения, внесенные в файлы конфигурации (то есть, postgresql.conf, pg_hba.conf и pg_ident.conf), поскольку они редактируются вручную, а не через операции SQL. Возможно, потребуется хранить файлы конфигурации в месте, которое будет резервироваться регулярными процедурами резервного копирования файловой системы. См. раздел «Расположение файлов» для получения информации о том, как переместить файлы конфигурации.

Команда или функция архивирования вызывается только для завершенных сегментов WAL. Поэтому, если сервер генерирует лишь небольшой объем трафика WAL (или имеет периоды простоя, когда он это делает), между завершением транзакции и ее безопасной записью в хранилище архива может пройти длительное время. Чтобы ограничить возраст незаархивированных данных, можно установить параметр archive_timeout, чтобы заставить сервер переключаться на новый файл сегмента WAL не реже этого значения. Обратите внимание, что заархивированные файлы, которые были заархивированы раньше из-за принудительного переключения, все равно имеют ту же длину, что и полностью заполненные файлы. Поэтому неразумно устанавливать очень короткое значение archive_timeout – это приведет к раздуванию хранилища архивов. Разумное значение для archive_timeout – одна минута или около того.

Кроме того, если нужно обеспечить, чтобы только что завершенная транзакция была заархивирована как можно скорее, можно вручную принудительно переключить сегмент с помощью pg_switch_wal. Другие служебные функции, связанные с управлением WAL, перечислены в таблице «Функции управления резервным копированием».

Когда wal_level установлен в minimal некоторые команды SQL оптимизированы для предотвращения ведения журнала WAL, как описано в разделе «Отключение архивации WAL и потоковой репликации». Если архивация или потоковая репликация были включены во время выполнения одного из этих операторов, WAL не будет содержать достаточной информации для восстановления архива (восстановление после сбоя не затрагивается). По этой причине wal_level может быть изменен только при запуске сервера. Однако archive_command и archive_library могут быть изменены путем перезагрузки файла конфигурации. Если выполняется архивирование через оболочку и нужно временно остановить архивирование, один из способов сделать это – установить archive_command в пустую строку (''). Это приведет к накоплению файлов WAL в pg_wal/ до тех пор, пока не будет восстановлен рабочий archive_command.

Создание базовой резервной копии

Самый простой способ выполнить базовое резервное копирование – использовать инструмент pg_basebackup. Он может создать базовое резервное копирование либо в виде обычных файлов, либо в виде архива tar. Если требуется больше гибкости, чем может предоставить pg_basebackup, также можно сделать базовое резервное копирование с использованием низкоуровневого API (см. раздел ниже).

Нет необходимости беспокоиться о том, сколько времени займет создание базовой копии. Однако, если обычно запускается сервер с отключенной функцией full_page_writes, то можно заметить снижение производительности во время выполнения резервного копирования, поскольку full_page_writes эффективно включается во время режима резервного копирования.

Чтобы воспользоваться резервной копией, нужно будет сохранить все файлы сегментов WAL, созданные во время и после резервного копирования файловой системы. Чтобы помочь в этом, процесс создания базовой копии создает файл истории резервных копий, который немедленно сохраняется в область архива WAL. Этот файл называется именем первого файла сегмента WAL, который необходим для резервного копирования файловой системы. Например, если начальный файл WAL – это 0000000100001234000055CD, то файл истории резервного копирования будет называться что-то вроде 0000000100001234000055CD.007C9330.backup (вторая часть имени файла обозначает точное положение внутри файла WAL и обычно может быть проигнорирована). Как только будет безопасно заархивирована копия файловой системы и файлы сегментов WAL, использованные при создании резервной копии (как указано в файле истории резервного копирования), все заархивированные сегменты WAL с числовыми именами, меньшими по номеру, уже не нужны для восстановления резервной копии файловой системы и могут быть удалены. Тем не менее, следует рассмотреть возможность сохранения нескольких наборов резервных копий, чтобы абсолютно точно убедиться, что можно восстановить данные.

Файл истории резервных копий – это просто небольшой текстовый файл. Он содержит строку метки, которую дали pg_basebackup, а также время начала и окончания и сегменты WAL резервной копии. Если использовалась метка для идентификации связанного файла дампа, то архивного файла истории достаточно, чтобы сообщить, какой файл дампа следует восстановить.

Поскольку нужно хранить все заархивированные файлы WAL с момента последнего полного резервного копирования, интервал между полными резервными копиями обычно должен быть выбран исходя из того, сколько места для хранения можно потратить на заархивированные файлы WAL. Также нужно учитывать, как долго имеется возможность тратить время на восстановление, если потребуется восстановление – системе придется воспроизвести все эти сегменты WAL, и это может занять некоторое время, если с момента последней полной резервной копии прошло много времени.

Создание инкрементальной резервной копии

Можно использовать команду pg_basebackup для создания инкрементального резервного копирования, указав опцию --incremental. Необходимо предоставить в качестве аргумента опции --incremental манифест резервной копии более ранней версии от того же самого сервера. В полученной резервной копии файлы, не относящиеся к отношениям, будут включены целиком, однако некоторые файлы отношений могут быть заменены меньшими инкрементальными файлами, которые содержат только блоки, изменившиеся с момента предыдущей резервной копии, и достаточный объем метаданных для реконструкции текущей версии файла.

Для определения блоков, которые необходимо зарезервировать, сервер использует сводки WAL, которые хранятся в директории данных, внутри каталога pg_wal/summaries. Если необходимые сводочные файлы отсутствуют, попытка создания инкрементальной резервной копии потерпит неудачу. Сводки, присутствующие в данном каталоге, должны охватывать все позиции LSN от начальной позиции предыдущего резервного копирования до начальной позиции текущего резервного копирования. Поскольку сервер ищет сводки WAL сразу после установления начальной позиции текущего резервного копирования, необходимые сводочные файлы, вероятно, не появятся мгновенно на диске, но сервер подождет появления любых отсутствующих файлов. Это также помогает, если процесс суммирования WAL отстает. Однако, если требуемые файлы уже были удалены или если процесс суммирования WAL не успевает достаточно быстро, создание инкрементальной резервной копии завершится ошибкой.

При восстановлении инкрементальной резервной копии потребуется не только сама инкрементальная резервная копия, но и все предыдущие резервные копии, необходимые для предоставления блоков, пропущенных в инкрементальной резервной копии. Дополнительную информацию об этом требовании см. в описании инструмента pg_combinebackup. Обратите внимание, что существуют ограничения на использование pg_combinebackup при изменении состояния контрольной суммы кластера; см. раздел ограничения pg_combinebackup.

Обратите внимание, что все требования для использования полной резервной копии также применимы и к инкрементальным резервным копиям. Например, все равно понадобятся все файлы сегментов WAL, созданные во время и после резервного копирования файловой системы, а также любые соответствующие файлы истории WAL. И все равно нужно будет создать файл recovery.signal (или standby.signal) и выполнить восстановление, как описано в разделе Восстановление с использованием непрерывного архивного резервного копирования. Требование иметь доступные ранние резервные копии во время восстановления и использовать pg_combinebackup является дополнительным требованием поверх всего остального. Учтите, что PostgreSQL не имеет встроенного механизма для выяснения, какие резервные копии все еще необходимы в качестве основы для последующего восстановления инкрементальных резервных копий. Необходимо отслеживать отношения между своими полными и инкрементальными резервными копиями и обязательно убедитесь, что старые резервные копии не удаляются, если они могут понадобиться при последующем восстановлении инкрементальных резервных копий.

Постепенные резервные копии обычно имеют смысл только для относительно больших баз данных, где значительная часть данных не изменяется или изменяется медленно. Для небольшой базы данных проще игнорировать существование постепенных резервных копий и просто создавать полные резервные копии, которые проще управлять. Для большой базы данных, вся информация которой интенсивно модифицируется, размер постепенной резервной копии будет ненамного меньше полной резервной копии.

Постепенная резервная копия возможна только в том случае, если воспроизведение начнется с более поздней контрольной точки по сравнению с предыдущей резервной копией, от которой она зависит. Если создается постепенная резервная копия на основном сервере, это условие всегда выполняется, поскольку каждая резервная копия запускает новую контрольную точку. На подчиненном сервере воспроизведение начинается с последней точки перезапуска. Следовательно, создание постепенной резервной копии сервера-подчиненного может завершиться неудачей, если после предыдущей резервной копии было очень мало активности, так как новая точка перезапуска могла не быть создана.

Создание резервной копии базы данных с использованием низкоуровневого API

Вместо того чтобы делать полную или постепенную базовую резервную копию с помощью pg_basebackup, можно создать базовую резервную копию с использованием низкоуровневого API. Эта процедура содержит несколько дополнительных шагов по сравнению с методом pg_basebackup, но относительно проста. Очень важно, чтобы шаги выполнялись последовательно, а успех шага проверялся перед переходом к следующему шагу.

Несколько резервных копий могут выполняться одновременно (как те, которые были начаты с использованием этого API для резервного копирования, так и те, которые были запущены с помощью pg_basebackup):

  1. Убедитесь, что архивирование WAL включено и работает.

  2. Подключитесь к серверу (неважно какая база данных) как пользователь с правами для запуска pg_backup_start (суперпользователь или пользователь, которому было предоставлено EXECUTE на функцию), и выполните команду:

    SELECT pg_backup_start(label => 'label', fast => false);

    где label - любая строка, которую хотите использовать для уникального обозначения этой операции резервного копирования. Подключение, вызывающее pg_backup_start, должно поддерживаться до конца резервного копирования, иначе резервное копирование будет автоматически прервано.

    Резервное копирование всегда начинается в начале контрольной точки. По умолчанию pg_backup_start будет ожидать завершения следующей регулярно запланированной контрольной точки, что может занять много времени (см. параметры конфигурации checkpoint_timeout и checkpoint_completion_target). Обычно это предпочтительнее, так как минимизирует влияние на работающую систему. Если нужно начать резервное копирование как можно скорее, передайте true в качестве второго параметра функции pg_backup_start, и она запросит немедленную контрольную точку, которая завершится как можно быстрее с использованием максимально возможного объема ввода-вывода.

  3. Выполните резервное копирование с использованием любого удобного инструмента для резервного копирования файловой системы, такого как tar или cpio (не pg_dump или pg_dumpall). Нет необходимости останавливать нормальную работу базы данных во время выполнения этой операции. См. раздел «Резервное копирование каталога данных» для получения информации о том, что следует учитывать при выполнении этого резервного копирования.

  4. В том же подключении, как и раньше, выполните команду:

    SELECT * FROM pg_backup_stop(wait_for_archive => true);

    При этом сервер выйдет из режима резервного копирования. На основном сервере также выполнится автоматический переход к следующему сегменту WAL. На резервном сервере невозможно автоматически переключить сегменты WAL, поэтому можно запустить pg_switch_wal на основном сервере для выполнения ручного переключения. Причина перехода заключается в том, чтобы подготовить последний записанный файл сегмента WAL за интервал резервного копирования для архивации.

    pg_backup_stop вернет одну строку с тремя значениями. Второе из этих полей должно быть записано в файл с именем backup_label в корневом каталоге резервной копии. Третье поле должно быть записано в файл с именем tablespace_map, если это поле не пустое. Эти файлы имеют решающее значение для работы резервного копирования и должны быть записаны побитово без изменений, что может потребовать открытия файла в двоичном режиме.

  5. Как только файлы сегментов WAL, активные во время резервного копирования, заархивированы, процедура резервного копирования будет завершена. Файл, идентифицированный первым возвращаемым значением pg_backup_stop, является последним сегментом, который требуется для формирования полного набора файлов резервной копии. На основном сервере, если archive_mode включен и параметр wait_for_archive равен true, pg_backup_stop не возвращает управление до тех пор, пока последний сегмент не будет заархивирован. На резервном сервере archive_mode должен быть установлен в значение always, чтобы pg_backup_stop мог ожидать завершения операции. Архивация этих файлов происходит автоматически, так как уже настроен archive_command или archive_library. В большинстве случаев это происходит быстро, но рекомендуется контролировать систему архивации, чтобы убедиться в отсутствии задержек. Если процесс архивации отстает из-за сбоев команды архивации или библиотеки, он будет продолжать попытки до успешного завершения архивации и завершения резервного копирования. Если нужно установить ограничение по времени выполнения pg_backup_stop, установите соответствующее значение statement_timeout, но имейте в виду, что если pg_backup_stop завершится из-за этого, резервная копия может оказаться недействительной.

    Если процесс резервного копирования контролирует и обеспечивает успешную архивацию всех необходимых файлов сегментов WAL, то параметр wait_for_archive (по умолчанию равный true) можно установить в false, чтобы pg_backup_stop завершилась сразу, как только в WAL будет помещена запись о завершении копирования. По умолчанию pg_backup_stop ожидает завершения архивации всего WAL, что может занять некоторое время. Этот вариант следует использовать с осторожностью: если архивация WAL контролируется неправильно, резервная копия может не содержать все файлы WAL и поэтому будет неполной и непригодной для восстановления.

Резервное копирование каталога данных

Некоторые инструменты резервного копирования файловой системы выдают предупреждения или сообщения об ошибках, если файлы, которые они пытаются скопировать, изменяются во время процесса копирования. При создании базовой резервной копии активной базы данных такая ситуация нормальна и не является ошибкой. Однако необходимо убедиться, что можно отличить такие жалобы от настоящих ошибок. Например, некоторые версии rsync возвращают отдельный код выхода для случая «исчезнувшие исходные файлы», и возможно написать сценарий-драйвер, который примет этот код выхода как случай отсутствия ошибки. Также некоторые версии GNU tar возвращают код ошибки, неотличимый от фатальной ошибки, если файл был усечен во время копирования его программой tar. К счастью, начиная с версии 1.16, GNU tar завершает работу с кодом 1, если файл был изменен во время резервного копирования, и с кодом 2 при возникновении других ошибок. Начиная с версии GNU tar 1.23 и выше, можно использовать параметры предупреждений --warning=no-file-changed --warning=no-file-removed, чтобы скрыть связанные с этим сообщения предупреждения.

Убедитесь, что резервная копия включает все файлы под каталогом кластера баз данных (например, /usr/local/pgsql/data). Если используются табличные пространства, которые находятся не внутри этого каталога, не забудьте включить и их в резервную копию (также важно, чтобы при создании резервной копии символьные ссылки сохранялись как ссылки, иначе табличные пространства будут повреждены при восстановлении).

Однако следует исключить из резервной копии файлы внутри подкаталога pg_wal/ кластера. Эта небольшая корректировка стоит того, поскольку она снижает риск ошибок при восстановлении. Это легко устроить, если pg_wal/ является символической ссылкой, указывающей куда-то за пределы каталога кластера, что является обычной настройкой в любом случае по причинам производительности. Возможно, также потребуется исключить postmaster.pid и postmaster.opts, которые регистрируют информацию о запущенном postmaster, а не о postmaster, который в конечном итоге будет использовать эту резервную копию (эти файлы могут запутать pg_ctl).

Часто хорошей идеей является также исключение файлов внутри каталога pg_replslot/ кластера из резервной копии, чтобы слоты репликации, существующие на основном сервере, не стали частью резервной копии. В противном случае последующее использование резервной копии для создания резервного сервера может привести к неопределенному сохранению файлов WAL на резервном сервере и, возможно, раздуванию на основном сервере, если включена обратная связь с горячим резервированием, потому что клиенты, использующие эти слоты репликации, будут продолжать подключаться к основным слотам и обновлять их, а не к резервным. Даже если резервная копия предназначена только для использования при создании нового основного сервера, копирование слотов репликации не ожидается быть особенно полезным, так как содержимое этих слотов, вероятно, сильно устареет к тому времени, когда новый основной сервер начнет работу.

Содержание каталогов pg_dynshmem/, pg_notify/, pg_serial/, pg_snapshots/, pg_stat_tmp/ и pg_subtrans/ (но не сами каталоги) можно исключить из резервной копии, так как они будут инициализированы при запуске postmaster.

Любой файл или каталог, начинающийся с pgsql_tmp , может быть исключен из резервной копии. Эти файлы удаляются при запуске postmaster, а каталоги будут воссозданы по мере необходимости.

Файлы pg_internal.init могут быть исключены из резервной копии всякий раз, когда обнаруживается файл с таким именем. Эти файлы содержат кешируемые данные отношений, которые всегда перестраиваются при восстановлении.

Файл метки резервного копирования включает строку метки, которая была присвоена pg_backup_start, а также время, когда было выполнено pg_backup_start, и имя начального файла WAL. В случае путаницы, следовательно, можно заглянуть внутрь файла резервной копии и точно определить, из какой сессии резервного копирования был получен дамп-файл. Файл карты табличных пространств содержит символические имена ссылок, существующие в каталоге pg_tblspc/, и полный путь каждой символической ссылки. Эти файлы предназначены не только для информации, их наличие и содержимое критически важны для правильной работы процесса восстановления системы.

Также возможно создать резервную копию, пока сервер остановлен. В этом случае, очевидно, нельзя использовать pg_backup_start или pg_backup_stop , и поэтому придется самостоятельно отслеживать, какая резервная копия является какой и насколько далеко назад идут связанные с ней файлы WAL. Обычно лучше следовать приведенной выше процедуре непрерывного архивирования.

Восстановление с использованием непрерывного архивного резервного копирования

Допустим, худшее случилось и нужно восстановить базу данных из резервной копии. Порядок действий:

  1. Остановите сервер, если он запущен.

  2. Если есть место для этого, скопируйте всю директорию данных кластера и любые табличные пространства во временное расположение на случай, если позже они понадобятся. Обратите внимание, что эта мера предосторожности потребует наличия достаточного свободного места на диске для хранения двух копий существующей базы данных. Если недостаточно места, нужно хотя бы сохранить содержимое подкаталога pg_wal кластера, так как он может содержать файлы WAL, которые не были архивированы до падения системы.

  3. Удалите все существующие файлы и подкаталоги из каталога данных кластера и корневых каталогов любых табличных пространств, которые используете.

  4. Если восстанавливается полная резервная копия, можно восстановить файлы базы данных непосредственно в целевые каталоги. Убедитесь, что они восстановлены с правильными правами собственности (пользователь системы базы данных, а не root!) и с правильными разрешениями. Если используете табличные пространства, убедитесь, что символические ссылки в pg_tblspc/ были правильно восстановлены.

  5. Если восстанавливаете инкрементную резервную копию, то потребуется восстановить инкрементную резервную копию и все предыдущие резервные копии, от которых она прямо или косвенно зависит, на машину, где выполняется восстановление. Эти резервные копии должны быть помещены в отдельные каталоги, а не в целевые каталоги, куда должен попасть работающий сервер. После того, как это будет сделано, используйте pg_combinebackup, чтобы извлечь данные из полной резервной копии и всех последующих инкрементных резервных копий и записать синтетическую полную резервную копию в целевые каталоги. Как указано выше, проверьте правильность разрешений и ссылок на табличное пространство.

  6. Удалите любые присутствующие в pg_wal/ файлы; они пришли из резервной копии файловой системы и поэтому, вероятно, устарели, а не актуальны. Если вообще не архивировали pg_wal/, создайте его заново с соответствующими разрешениями, внимательно убедившись, что он снова установлен как символическая ссылка, если ранее была такая настройка.

  7. Если есть неразархивированные сегменты файлов WAL, сохраненные на шаге 2, скопируйте их в pg_wal/. Лучше всего копировать их, а не перемещать, так что исходные файлы остаются неизмененными, если возникает проблема и нужно начать сначала.

  8. Установите параметры конфигурации восстановления в postgresql.conf и создайте файл recovery.signal в каталоге данных кластера. Возможно, также стоит временно изменить pg_hba.conf, чтобы предотвратить подключение обычных пользователей до тех пор, пока не убедитесь, что восстановление прошло успешно.

  9. Запустите сервер. Сервер перейдет в режим восстановления и начнет считывать необходимые ему заархивированные файлы WAL. В случае прерывания восстановления по причине внешней ошибки сервер можно просто перезапустить, и он продолжит восстановление. По завершении процесса восстановления сервер удалит recovery.signal (чтобы избежать случайного повторного входа в режим восстановления позже) и затем приступит к нормальной работе с базой данных.

  10. Проверьте содержимое базы данных, чтобы убедиться, что восстановление произошло до желаемого состояния. Если нет, вернитесь к шагу 1. Если все хорошо, разрешите пользователям подключаться, восстановив pg_hba.conf в нормальное состояние.

Ключевым моментом всего этого является настройка конфигурации восстановления, которая описывает, как нужно восстановить данные и насколько далеко должно пройти восстановление. Единственное, что необходимо указать, это restore_command, который сообщает PostgreSQL, как извлекать заархивированные сегменты файлов WAL. Как и archive_command, это строка команды оболочки. Она может содержать %f, которое заменяется именем желаемого файла журнала, и %p, которое заменяется путем к имени файла, по которому следует копировать файл журнала (путь относителен к текущему рабочему каталогу, т.е. к каталогу данных кластера). Напишите %%, если нужно встроить фактический символ % в команду. Самая простая полезная команда выглядит примерно так:

restore_command = 'cp /mnt/server/archivedir/%f %p'

Команда будет копировать ранее заархивированные сегменты WAL из каталога /mnt/server/archivedir. Конечно, можно использовать что-то гораздо более сложное, возможно, даже сценарий командной оболочки, который запрашивает у оператора монтирование соответствующей ленты.

Важно, чтобы команда возвращала ненулевой статус выхода при сбое. Команда будет вызвана с запросом файлов, которые отсутствуют в архиве. Она должна вернуть ненулевое значение, когда ее об этом попросят. Это не условие ошибки. Исключение составляет то, что если команда была завершена сигналом (кроме SIGTERM, который используется в рамках завершения работы сервера базы данных) или ошибкой оболочки (например, команда не найдена), тогда восстановление будет прервано, и сервер не запустится.

Не все запрошенные файлы будут файлами сегмента WAL, также следует ожидать запросов на файлы с суффиксом .history. Имейте в виду, что базовое имя пути %p будет отличаться от %f, не ожидайте, что они будут взаимозаменяемыми.

Сегменты WAL, которые не могут быть найдены в архиве, будут искаться в pg_wal/, это позволяет использовать недавние неразархивированные сегменты. Однако сегменты, доступные из архива, будут использоваться предпочтительно перед файлами в pg_wal/.

Обычно восстановление будет проходить через все доступные сегменты WAL, тем самым восстанавливая базу данных до текущей точки во времени (или насколько возможно с учетом доступных сегментов WAL). Поэтому нормальное восстановление завершится сообщением о «файл не найден», точный текст сообщения об ошибке зависит от выбора restore_command. Также можно увидеть сообщение об ошибке в начале восстановления для файла с именем что-то вроде 00000001.history. Это тоже нормально и не указывает на проблему в простых ситуациях восстановления (см. раздел «Таймлайны»).

Если требуется восстановление до какой-то предыдущей точки во времени (скажем, прямо перед тем, как младший администратор баз данных удалил основную таблицу транзакций), просто укажите необходимую точку остановки. Можно указать точку останова, известную как «целевой объект восстановления», либо по дате/времени, либо по названной точке восстановления, либо по завершению определенного идентификатора транзакции. На момент написания этой статьи только варианты с датой/временем и именованной точкой восстановления очень удобны, поскольку нет инструментов, которые помогли бы определить с какой-либо точностью, какой идентификатор транзакции использовать.

Примечание

Точка останова должна быть после окончания основного резервного копирования, т.е. после завершения pg_backup_stop. Нельзя использовать основное резервное копирование для восстановления до момента, когда это резервное копирование было в процессе выполнения (чтобы восстановиться до такого времени, необходимо вернуться к предыдущему основному резервному копированию и продолжить работу оттуда).

Если восстановление обнаруживает поврежденные данные WAL, восстановление будет приостановлено на этом этапе, и сервер не запустится. В таком случае процесс восстановления может быть запущен заново с самого начала, указав «цель восстановления» до момента повреждения, чтобы восстановление могло завершиться нормально. Если восстановление не удается из-за внешней причины, такой как системный сбой или если архив WAL стал недоступен, то восстановление можно просто перезапустить, и оно начнется почти с того места, где произошел сбой. Перезапуск восстановления работает так же, как контрольная точка при нормальной работе: сервер периодически принудительно записывает все свои состояния на диск, а затем обновляет файл pg_control , чтобы указать, что уже обработанные данные WAL больше не нужно просматривать.

Таймлайны

Возможность восстановления базы данных до предыдущей точки во времени создает некоторые сложности, которые схожи с научно-фантастическими историями о путешествиях во времени и параллельных вселенных. Например, в оригинальной истории базы данных предположим, что удалили критическую таблицу в 17:15 во вторник вечером, но не осознали свою ошибку до обеда в среду. Не теряя самообладания, достаете резервную копию, восстанавливаетесь к моменту времени 17:14 вечера вторника и снова работаете. В этой истории вселенной баз данных никогда не удаляли таблицу. Но предположим, что позже приходит понимание, что это была не такая уж великая идея, и хотели бы вернуться к утру среды в оригинальной истории. Не получится сделать этого, если база данных перезаписала некоторые файлы сегментов WAL, которые привели к тому времени, к которому теперь нужно вернуться. Таким образом, чтобы избежать этого, нужно различать серию записей WAL, созданных после того, как выполнено восстановление по состоянию на определенный момент времени, от тех, которые были созданы в исходной истории базы данных.

Для решения этой проблемы PostgreSQL имеет понятие линий времени. Всякий раз, когда завершается архивное восстановление, создается новая линия времени, которая идентифицирует серию записей WAL, созданных после этого восстановления. Идентификатор линии времени является частью имени файла сегмента WAL, поэтому новая линия времени не перезаписывает данные WAL, созданные предыдущими линиями времени. Например, в имени файла WAL 0000000100001234000055CD ведущий 00000001 представляет собой идентификатор линии времени в шестнадцатеричном формате. (Обратите внимание, что в других контекстах, таких как сообщения журнала сервера, идентификаторы линий времени обычно отображаются в десятичном виде.)

Каждый раз при создании новой временной шкалы PostgreSQL создает файл «истории временных шкал», который показывает, от какой временной шкалы она была создана и когда. Эти файлы истории необходимы для того, чтобы система могла выбрать правильный сегментный файл WAL при восстановлении из архива, содержащего несколько временных шкал. Поэтому они архивируются в область архива WAL так же, как и сегментные файлы WAL. Файлы истории представляют собой небольшие текстовые файлы, поэтому их хранение не требует больших затрат и вполне уместно (в отличие от сегментных файлов, которые имеют большой размер). Можно, если хотите, добавить комментарии к файлу истории, чтобы записать свои собственные заметки о том, как и почему была создана эта конкретная временная шкала. Такие комментарии будут особенно ценными, когда у будет множество различных временных шкал в результате экспериментов.

По умолчанию восстановление происходит до последней найденной во временной шкале архива. Если нужно восстановить временную шкалу, которая была актуальной на момент создания резервной копии базы данных или в определенную дочернюю временную шкалу (то есть требуется вернуться к некоторому состоянию, которое само было создано после попытки восстановления), то нужно указать current или идентификатор целевой временной шкалы в recovery_target_timeline. Нельзя восстанавливать временные шкалы, которые ответвились раньше, чем базовая резервная копия.

Советы и примеры

Ниже приведены некоторые советы по настройке непрерывного архивирования.

Автономные горячие резервные копии

Можно использовать средства резервного копирования PostgreSQL, чтобы создать автономные горячие копии. Это такие копии, которые нельзя использовать для восстановления до определенной точки во времени, но обычно они намного быстрее для создания и восстановления копий, чем дампы pg_dump. Они также значительно больше, чем дампы pg_dump, поэтому в некоторых случаях преимущество в скорости может быть сведено к нулю.

Как и в случае с базовыми копиями, самый простой способ сделать автономную горячую копию – это воспользоваться инструментом pg_basebackup. Если включить параметр -X, когда будете вызывать его, весь журнал записи вперед, необходимый для использования этой копии, будет автоматически включен в эту копию, и никаких специальных действий для ее восстановления не потребуется.

Архивные журналы в сжатом виде

Если размер хранилища архива вызывает беспокойство, можно использовать gzip для сжатия архивных файлов:

archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'

Тогда нужно будет использовать gunzip во время восстановления:

restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'

Сценарии archive_command

Многие люди предпочитают использовать сценарии для определения своих archive_command, чтобы их запись postgresql.conf выглядела очень просто:

archive_command = 'local_backup_script.sh "%p" "%f"'

Использование отдельного файла сценария рекомендуется каждый раз, когда используется более одной команды в процессе архивирования. Это позволяет управлять всей сложностью внутри сценария, который может быть написан на популярном языке сценариев, таком как bash или perl.

Примеры требований, которые могут быть решены в рамках сценария, включают:

  • Копирование данных в безопасное удаленное хранилище данных.
  • Пакетная передача файлов WAL таким образом, чтобы они передавались каждые три часа, а не по одному за раз.
  • Взаимодействие с другим программным обеспечением для резервного копирования и восстановления.
  • Взаимодействие с программным обеспечением мониторинга для сообщения об ошибках.
Совет

При использовании сценария archive_command желательно включить logging_collector. Любые сообщения, записанные в stderr из сценария, затем появятся в журнале сервера баз данных, что позволяет легко диагностировать сложные конфигурации в случае их сбоя.

Ограничения

На момент написания этого документа существует несколько ограничений техники непрерывного архивирования. Вероятно, они будут исправлены в будущих выпусках:

  • Если команда CREATE DATABASE выполняется во время создания резервной копии базы данных, а затем шаблонная база данных, которую копирует CREATE DATABASE, изменяется во время выполнения резервного копирования базы данных, возможно, что восстановление приведет к тому, что эти изменения будут перенесены в созданную базу данных. Это, конечно, нежелательно. Чтобы избежать этого риска, лучше не изменять никакие шаблоны баз данных при создании резервной копии базы данных.
  • CREATE TABLESPACE команды регистрируются в WAL с буквальным абсолютным путем и поэтому будут воспроизведены как создания табличных пространств с тем же абсолютным путем. Это может быть нежелательно, если WAL воспроизводится на другом компьютере. Это может быть опасно даже в том случае, если WAL воспроизводится на той же машине, но в новый каталог данных: при воспроизведении все равно будет перезаписано содержимое исходного табличного пространства. Чтобы избежать потенциальных проблем такого рода, наилучшей практикой является создание новой базовой резервной копии после создания или удаления табличных пространств.

Также следует отметить, что стандартный формат WAL довольно громоздкий, поскольку содержит множество снимков дисковых страниц. Эти снимки страниц предназначены для поддержки восстановления после сбоев, так как может потребоваться исправить частично записанные дисковые страницы. В зависимости от аппаратного и программного обеспечения системы риск частичной записи может быть достаточно мал, чтобы его можно было игнорировать, в этом случае можно значительно уменьшить общий объем архивированных WAL, отключив снимки страниц с помощью параметра full_page_writes (прочитайте примечания и предупреждения в разделе «Надежность и журнал предварительной записи», прежде чем это сделать). Отключение снимков страниц не препятствует использованию WAL для операций PITR. Одной из областей для будущего развития является сжатие архивированных данных WAL путем удаления ненужных копий страниц, даже когда full_page_writes включен. Тем временем администраторы могут сократить количество снимков страниц, включенных в WAL, увеличив параметры интервала контрольных точек настолько, насколько это возможно.