25.3. Непрерывное архивирование и восстановление на момент времени (Point-in-Time Recovery, PITR)

Всё время в процессе работы Postgres Pro ведёт журнал предзаписи (WAL), который расположен в подкаталоге pg_wal/ каталога с данными кластера баз данных. В этот журнал записываются все изменения, вносимые в файлы данных. Прежде всего, журнал существует для безопасного восстановления после краха сервера: если происходит крах, целостность СУБД может быть восстановлена в результате «воспроизведения» записей, зафиксированных после последней контрольной точки. Однако наличие журнала делает возможным использование третьей стратегии копирования баз данных: можно сочетать резервное копирование на уровне файловой системы с копированием файлов WAL. Если потребуется восстановить данные, мы можем восстановить копию файлов, а затем воспроизвести журнал из скопированных файлов WAL, и таким образом привести систему в нужное состояние. Такой подход более сложен для администрирования, чем любой из описанных выше, но он имеет значительные преимущества:

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

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

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

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

Примечание

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

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

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

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

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

При архивировании данных WAL необходимо считывать содержимое каждого файла-сегмента, как только он заполняется, и сохранять эти данные куда-то, прежде чем файл-сегмент будет переработан и использован повторно. В зависимости от применения и доступного аппаратного обеспечения, возможны разные способы «сохранить данные куда-то»: можно скопировать файлы-сегменты в смонтированный по NFS каталог на другую машину, записать их на ленту (убедившись, что у вас есть способ идентифицировать исходное имя каждого файла) или собрать их в пакет и записать на CD, либо какие-то совсем другие варианты. Чтобы у администратора баз данных была гибкость в этом плане, Postgres Pro пытается не делать каких-либо предположений о том, как будет выполняться архивация. Вместо этого, Postgres Pro позволяет администратору указать команду оболочки, которая будет запускаться для копирования завершённого файла-сегмента в нужное место. Эта команда может быть простой как cp, а может вызывать сложный скрипт оболочки — это решать вам.

Чтобы включить архивирование WAL, установите в параметре конфигурации wal_level уровень replica (или выше), в archive_mode — значение on, и задайте желаемую команду оболочки в параметре archive_command. На практике эти параметры всегда задаются в файле 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

Подобная команда будет генерироваться для каждого следующего архивируемого файла.

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

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

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

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

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

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

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

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

Обратите внимание, что хотя архивирование WAL позволяет сохранить любые изменения данных, произведённые в базе данных Postgres Pro, оно не затрагивает изменения, внесённые в конфигурационные файлы (такие как postgresql.conf, pg_hba.conf и pg_ident.conf), поскольку эти изменения выполняются вручную, а не через SQL. Поэтому имеет смысл разместить конфигурационные файлы там, где они будут заархивированы обычными процедурами копирования файлов. Как перемещать конфигурационные файлы, рассказывается в Разделе 19.2.

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

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

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

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

Проще всего получить базовую резервную копию, используя программу pg_basebackup. Эта программа сохраняет базовую копию в виде обычных файлов или в архиве tar. Если гибкости pg_basebackup не хватает, вы также можете получить базовую резервную копию, используя низкоуровневый API (см. Подраздел 25.3.3).

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

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

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

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

25.3.3. Создание базовой резервной копии через низкоуровневый API

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

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

25.3.3.1. Немонопольное резервное копирование на низком уровне

Немонопольное резервное копирование позволяет параллельно запускать другие процессы копирования (используя тот же API или pg_basebackup).

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

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

    SELECT pg_start_backup('label', false, false);

    где label — любая метка, по которой можно однозначно идентифицировать данную операцию резервного копирования. Соединение, через которое вызывается pg_start_backup, должно поддерживаться до окончания резервного копирования, иначе этот процесс будет автоматически прерван.

    По умолчанию pg_start_backup может выполняться длительное время. Это объясняется тем, что функция выполняет контрольную точку, а операции ввода/вывода, требуемые для этого, распределяются в интервале времени, по умолчанию равном половине интервала между контрольными точками (см. параметр checkpoint_completion_target). Обычно это вполне приемлемо, так как при этом минимизируется влияние на выполнение других запросов. Если же вы хотите начать резервное копирование максимально быстро, передайте во втором параметре true. В этом случае контрольная точка будет выполнена немедленно без ограничения объёма ввода/вывода.

    Третий параметр, имеющий значение false, указывает pg_start_backup начать немонопольное базовое копирование.

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

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

    SELECT * FROM pg_stop_backup(false, true);

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

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

  5. После этого останется заархивировать файлы сегментов WAL, активных во время создания резервной копии, и процедура резервного копирования будет завершена. Функция pg_stop_backup в первом значении результата указывает, какой последний сегмент требуется для формирования полного набора файлов резервной копии. На ведущем сервере, если включён режим архивации (параметр archive_mode) и аргумент wait_for_archive равен true, функция pg_stop_backup не завершится, пока не будет заархивирован последний сегмент. На ведомом значением archive_mode должно быть always, чтобы pg_stop_backup ожидала архивации. Эти файлы будут заархивированы автоматически, поскольку также должна быть настроена команда archive_command. Чаще всего это происходит быстро, но мы советуем наблюдать за системой архивации и проверять, не возникают ли задержки. Если архивирование остановится из-за ошибок команды архивации, попытки архивации будут продолжаться до успешного завершения, и только тогда резервное копирование окончится. Если вы хотите ограничить время выполнения pg_stop_backup, установите соответствующее значение в statement_timeout, но заметьте, что в случае прерывания pg_stop_backup по времени резервная копия может оказаться негодной.

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

25.3.3.2. Монопольное резервное копирование на низком уровне

Примечание

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

Монопольное резервное копирование во многом похоже на немонопольное, но имеет несколько важных отличий. Такое копирование можно произвести только на ведущем сервере, и оно исключает одновременное выполнение других процессов копирования. Более того, так как при таком копировании на ведущем создаётся файл с меткой резервного копирования, как описано ниже, сервер может не перезапуститься автоматически в случае сбоя. С другой стороны, ошибочное удаление этого файла из резервной копии или с ведомого сервера, что наблюдается нередко, может повлечь серьёзное повреждение данных. Если всё-таки необходимо использовать именно этот вариант, вы можете произвести следующие действия.

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

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

    SELECT pg_start_backup('label');

    где label — любая метка, по которой можно однозначно идентифицировать данную операцию резервного копирования. Функция pg_start_backup создаёт в каталоге кластера файл метки резервного копирования, называемый backup_label, в который помещается информация о резервной копии, включающая время начала и строку метки. Эта функция также создаёт в каталоге кластера файл карты табличных пространств, называемый tablespace_map, с информацией о символических ссылках табличных пространств в pg_tblspc/, если такие ссылки есть. Оба файла важны для целостности резервных копии и понадобятся при восстановлении.

    По умолчанию pg_start_backup может выполняться длительное время. Это объясняется тем, что функция выполняет контрольную точку, а операции ввода/вывода, требуемые для этого, распределяются в интервале времени, по умолчанию равном половине интервала между контрольными точками (см. параметр checkpoint_completion_target). Обычно это вполне приемлемо, так как при этом минимизируется влияние на выполнение других запросов. Если же вы хотите начать резервное копирование максимально быстро, выполните:

    SELECT pg_start_backup('label', true);

    При этом контрольная точка будет выполнена как можно скорее.

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

    Как отмечено выше, если в процессе резервного копирования произойдёт сбой сервера, попытки перезапустить его могут быть безуспешными, пока файл backup_label не будет вручную удалён из каталога PGDATA. Заметьте, что для восстановления резервной копии, наоборот, удалять файл backup_label категорически нельзя, иначе данные будут повреждены. Именно отсутствие чёткого понимания, когда следует удалять этот файл, является распространённой причиной повреждения данных при использовании этого метода. Поэтому важно не ошибиться, и удалять этот файл только на работающим ведущем сервере, но ни в коем случае не удалять его при восстановлении резервной копии или создании резервного сервера, даже если вы планируете впоследствии сделать его новым ведущим.

  4. Снова подключитесь к базе данных как пользователь с правами на выполнение pg_stop_backup (суперпользователь или пользователь, которому дано право EXECUTE для этой функции) и выполните команду:

    SELECT pg_stop_backup();

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

  5. После этого останется заархивировать файлы сегментов WAL, активных во время создания резервной копии, и процедура резервного копирования будет завершена. Функция pg_stop_backup возвращает указание на файл последнего сегмента, который требуется для формирования полного набора файлов резервной копии. Если включён режим архивации (параметр archive_mode), функция pg_stop_backup не завершится, пока не будет заархивирован последний сегмент. В этом случае файлы будут заархивированы автоматически, поскольку также должна быть настроена команда archive_command. Чаще всего это происходит быстро, но мы советуем наблюдать за системой архивации и проверять, не возникают ли задержки. Если архивирование остановится из-за ошибок команды архивации, попытки архивации будут продолжаться до успешного завершения, и только тогда резервное копирование окончится.

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

25.3.3.3. Копирование каталога данных

Некоторые средства резервного копирования файлов выдают предупреждения или ошибки, если файлы, которые они пытаются скопировать, изменяются в процессе копирования. При получении базовой резервной копии активной базы данных это вполне нормально и не является ошибкой. Однако вам нужно знать, как отличить ошибки такого рода от реальных ошибок. Например, некоторые версии rsync возвращают отдельный код завершения для ситуации «исчезнувшие исходные файлы», и вы можете написать управляющий скрипт, который примет этот код как не ошибочный. Также некоторые версии GNU tar возвращают код завершения, неотличимый от кода критической ошибки, если файл был усечён, когда tar копировал его. К счастью, GNU tar версий 1.16 и более поздних завершается с кодом 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/ (но не сами эти каталоги) можно исключить из резервной копии, так как оно будет инициализировано при запуске главного процесса. Если переменная stats_temp_directory установлена и указывает на подкаталог внутри каталога данных, содержимое этого подкаталога также можно не копировать.

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

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

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

Вы также можете создать резервную копию, когда сервер остановлен. В этом случае, вы, очевидно, не сможете вызвать pg_start_backup или pg_stop_backup, и следовательно, вам надо будет самостоятельно как-то идентифицировать резервные копии и понимать, какие файлы WAL должны быть заархивированы. Поэтому обычно всё-таки лучше следовать вышеописанной процедуре непрерывного архивирования.

25.3.4. Восстановление непрерывной архивной копии

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

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

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

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

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

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

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

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

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

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

Ключевой момент этой процедуры заключается в создании конфигурации восстановления, описывающей, как будет выполняться восстановление и до какой точки. Единственное, что совершенно необходимо задать — это команду restore_command, которая говорит Postgres Pro, как получать из архива файл-сегменты 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. Это также нормально и обычно не говорит о какой-либо проблеме при восстановлении в простых ситуациях; подробнее об этом рассказывается в Подразделе 25.3.5.

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

Примечание

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

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

25.3.5. Линии времени

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

Для решения этой проблемы в Postgres Pro есть такое понятие, как линия времени. Всякий раз, когда завершается восстановление из архива, создаётся новая линия времени, позволяющая идентифицировать последовательность записей WAL, добавленных после этого восстановления. Номер линии времени включается в имя файлов-сегментов WAL, так что файлы новой линии времени не перезаписывают файлы WAL, сгенерированные предыдущими линиями времени. Фактически это позволяет архивировать много различных линий времени. Хотя это может показаться бесполезной возможностью, на самом деле она часто бывает спасительной. Представьте, что вы не определились, какую точку времени выбрать для восстановления, и таким образом должны проводить восстановление методом проб и ошибок, пока не найдёте лучший момент для ответвления от старой истории. Без линий времени этот процесс быстро стал бы очень запутанным. А благодаря линиям времени, вы можете вернуться к любому предыдущему состоянию, включая состояния в ветках линий времени, покинутых ранее.

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

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

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

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

25.3.6.1. Обособленные горячие резервные копии

Средства резервного копирования Postgres Pro можно применять для создания обособленных горячих копий. Эти копии нельзя использовать для восстановления на момент времени, но создаются и восстанавливаются они обычно гораздо быстрее, чем дампы pg_dump. (Они также намного больше, чем дампы pg_dump, так что в некоторых случаях выигрыш в скорости может быть потерян.)

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

Если нужна дополнительная гибкость в процессе копирования файлов, создавать обособленные горячие копии можно также на более низком уровне. Чтобы подготовиться к получению такой копии на низком уровне, установите в wal_level уровень replica (или выше), в archive_mode значение on и настройте команду archive_command, которая будет выполнять архивацию, только когда существует файл-переключатель. Например:

archive_command = 'test ! -f /var/lib/pgsql/backup_in_progress || (test ! -f /var/lib/pgsql/archive/%f && cp %p /var/lib/pgsql/archive/%f)'

Данная команда выполнит архивацию, если будет существовать файл /var/lib/pgsql/backup_in_progress, а в противном случае просто вернёт нулевой код возврата (и тогда Postgres Pro сможет переработать ненужный файл WAL).

После такой подготовки резервную копию можно создать, например таким скриптом:

touch /var/lib/pgsql/backup_in_progress
psql -c "select pg_start_backup('hot_backup');"
tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/
psql -c "select pg_stop_backup();"
rm /var/lib/pgsql/backup_in_progress
tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/

Сначала создаётся файл-переключатель /var/lib/pgsql/backup_in_progress, включающий архивирование заполненных файлов WAL. По окончании резервного копирования файл-переключатель удаляется. Затем заархивированные файлы WAL тоже добавляются в резервную копию, так что в одном архиве tar оказывается и базовая резервная копия, и все требуемые файлы WAL. Пожалуйста, не забудьте добавить в ваши скрипты резервного копирования обработку ошибок.

25.3.6.2. Сжатие журналов в архиве

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

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

При этом для восстановления придётся использовать gunzip:

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

25.3.6.3. Скрипты archive_command

Многие в качестве команды archive_command используют скрипты, так что запись в postgresql.conf оказывается очень простой:

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

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

В частности, с помощью скриптов можно решить такие задачи:

  • Копирование данных в безопасное внешнее хранилище

  • Пакетная обработка файлов WAL, чтобы они передавались каждые три часа, а не по одному

  • Взаимодействие с другими приложениями резервного копирования и восстановления

  • Взаимодействие со средствами мониторинга, регистрация ошибок

Подсказка

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

25.3.7. Ограничения

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

  • Если во время создания базовой резервной копии выполняется команда CREATE DATABASE, а затем база-шаблон, задействованная в CREATE DATABASE, изменяется, пока продолжается копирование, возможно, что при восстановлении эти изменения распространятся также и на созданную базу данных. Конечно, это нежелательно. Во избежание подобных рисков, лучше всего не изменять никакие базы-шаблоны во время получения базовой резервной копии.

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

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