24.3. Непрерывное архивирование и восстановление на момент времени (Point-in-Time Recovery, PITR)
Всё время в процессе работы PostgreSQL ведёт журнал предзаписи (WAL), который расположен в подкаталоге pg_xlog/
каталога с данными кластера баз данных. В этот журнал записываются все изменения, вносимые в файлы данных. Прежде всего, журнал существует для безопасного восстановления после краха сервера: если происходит крах, целостность СУБД может быть восстановлена в результате «воспроизведения» записей, зафиксированных после последней контрольной точки. Однако наличие журнала делает возможным использование третьей стратегии копирования баз данных: можно сочетать резервное копирование на уровне файловой системы с копированием файлов WAL. Если потребуется восстановить данные, мы можем восстановить копию файлов, а затем воспроизвести журнал из скопированных файлов WAL, и таким образом привести систему в нужное состояние. Такой подход более сложен для администрирования, чем любой из описанных выше, но он имеет значительные преимущества:
В качестве начальной точки для восстановления необязательно иметь полностью согласованную копию на уровне файлов. Внутренняя несогласованность копии будет исправлена при воспроизведении журнала (практически то же самое происходит при восстановлении после краха). Таким образом, согласованный снимок файловой системы не требуется, вполне можно использовать tar или похожие средства архивации.
Поскольку при воспроизведении можно обрабатывать неограниченную последовательность файлов WAL, непрерывную резервную копию можно получить, просто продолжая архивировать файлы WAL. Это особенно ценно для больших баз данных, полные резервные копии которых делать как минимум неудобно.
Воспроизводить все записи WAL до самого конца нет необходимости. Воспроизведение можно остановить в любой точке и получить целостный снимок базы данных на этот момент времени. Таким образом, данная технология поддерживает восстановление на момент времени: можно восстановить состояние базы данных на любое время с момента создания резервной копии.
Если непрерывно передавать последовательность файлов WAL другому серверу, получившему данные из базовой копии того же кластера, получается система тёплого резерва: в любой момент мы можем запустить второй сервер и он будет иметь практически текущую копию баз данных.
Примечание
Программы pg_dump и pg_dumpall не создают копии на уровне файловой системы и не могут применяться как часть решения по непрерывной архивации. Создаваемые ими копии являются логическими и не содержат информации, необходимой для воспроизведения WAL.
Как и обычное резервное копирование файловой системы, этот метод позволяет восстанавливать только весь кластер баз данных целиком, но не его части. Кроме того, для архивов требуется большое хранилище: базовая резервная копия может быть объёмной, а нагруженные системы будут генерировать многие мегабайты трафика WAL, который необходимо архивировать. Тем не менее, этот метод резервного копирования предпочитается во многих ситуациях, где необходима высокая надёжность.
Для успешного восстановления с применением непрерывного архивирования (также называемого «оперативным резервным копированием» многими разработчиками СУБД), вам необходима непрерывная последовательность заархивированных файлов WAL, начинающаяся не позже, чем с момента начала копирования. Так что для начала вы должны настроить и протестировать процедуру архивирования файлов WAL до того, как получите первую базовую копию. Соответственно, сначала мы обсудим механику архивирования файлов WAL.
24.3.1. Настройка архивирования WAL
В абстрактном смысле, запущенная СУБД PostgreSQL производит неограниченно длинную последовательность записей WAL. СУБД физически делит эту последовательность на файлы-сегменты WAL, которые обычно имеют размер в 16 МиБ (хотя размер сегмента может быть изменён при сборке PostgreSQL). Файлы-сегменты получают цифровые имена, которые отражают их позицию в абстрактной последовательности WAL. Когда архивирование WAL не применяется, система обычно создаёт только несколько файлов-сегментов и затем «перерабатывает» их, меняя номер в имени более не нужного файла-сегмента на больший. Предполагается, что файлы-сегменты, чьё содержимое предшествует последней контрольной точке, уже не представляют интереса и могут быть переработаны.
При архивировании данных WAL необходимо считывать содержимое каждого файла-сегмента, как только он заполняется, и сохранять эти данные куда-то, прежде чем файл-сегмент будет переработан и использован повторно. В зависимости от применения и доступного аппаратного обеспечения, возможны разные способы «сохранить данные куда-то»: можно скопировать файлы-сегменты в смонтированный по NFS каталог на другую машину, записать их на ленту (убедившись, что у вас есть способ идентифицировать исходное имя каждого файла) или собрать их в пакет и записать на CD, либо какие-то совсем другие варианты. Чтобы у администратора баз данных была гибкость в этом плане, PostgreSQL пытается не делать каких-либо предположений о том, как будет выполняться архивация. Вместо этого, PostgreSQL позволяет администратору указать команду оболочки, которая будет запускаться для копирования завершённого файла-сегмента в нужное место. Эта команда может быть простой как cp
, а может вызывать сложный скрипт оболочки — это решать вам.
Чтобы включить архивирование WAL, установите в параметре конфигурации wal_level уровень archive
(или выше), в 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_xlog/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065
Подобная команда будет генерироваться для каждого следующего архивируемого файла.
Команда архивирования будет запущена от имени того же пользователя, от имени которого работает сервер PostgreSQL. Поскольку архивируемые последовательности файлов WAL фактически содержат всё, что есть в вашей базе данных, вам нужно будет защитить архивируемые данные от посторонних глаз; например, сохраните архив в каталог, чтение которого запрещено для группы и остальных пользователей.
Важно, чтобы команда архивирования возвращала нулевой код завершения, если и только если она завершилась успешно. Получив нулевой результат, PostgreSQL будет полагать, что файл успешно заархивирован и удалит его или переработает. Однако, ненулевой код состояния скажет PostgreSQL, что файл не заархивирован; попытки заархивировать его будут периодически повторяться, пока это не удастся.
Команда архивирования обычно разрабатывается так, чтобы не допускать перезаписи любых существующих архивных файлов. Это важная мера безопасности, позволяющая сохранить целостность архива в случае ошибки администратора (например, если архивируемые данные двух разных серверов будут сохраняться в одном каталоге).
Рекомендуется протестировать команду архивирования, чтобы убедиться, что она действительно не перезаписывает существующие файлы, и что она возвращает ненулевое состояние в этом случае. В показанной выше команде для Unix для этого добавлен отдельный шаг test
. На некоторых платформах Unix у cp
есть ключ -i
, который позволяет сделать то же, но менее явно; но не проверив, какой код состояния при этом возвращается, полагаться на этот ключ не следует. (В частности, GNU cp
возвратит нулевой код состояния, если используется ключ -i
и целевой файл существует, а это не то, что нужно.)
Разрабатывая схему архивирования, подумайте, что произойдёт, если команда архивирования начнёт постоянно выдавать ошибку, потому что требуется вмешательство оператора или для архивирования не хватает места. Например, это может произойти, если вы записываете архивы на ленточное устройство без механизма автозамены; когда лента заполняется полностью, больше ничего архивироваться не будет, пока вы не замените кассету. Вы должны убедиться, что любые возникающие ошибки или обращения к человеку (оператору), обрабатываются так, чтобы проблема решалась достаточно быстро. Пока она не разрешится, каталог pg_xlog/
продолжит наполняться файлами-сегментами WAL. (Если файловая система, в которой находится каталог pg_xlog/
заполнится до конца, PostgreSQL завершит свою работу аварийно. Зафиксированные транзакции не потеряются, но база данных не будет работать, пока вы не освободите место.)
Не важно, с какой скоростью работает команда архивирования, если только она не ниже средней скорости, с которой сервер генерирует записи WAL. Обычно работа продолжается, даже если процесс архивирования немного отстаёт. Если же архивирование отстаёт значительно, это приводит к увеличению объёма данных, которые могут быть потеряны в случае аварии. При этом каталог pg_xlog/
будет содержать большое количество ещё не заархивированных файлов-сегментов, которые в конце концов могут занять всё доступное дисковое пространство. Поэтому рекомендуется контролировать процесс архивации и следить за тем, чтобы он выполнялся как задумано.
При написании команды архивирования вы должны иметь в виду, что имена файлов для архивирования могут иметь длину до 64 символов и содержать любые комбинации из цифр, точек и букв ASCII. Сохранять исходный относительный путь (%p
) необязательно, но необходимо сохранять имя файла (%f
).
Обратите внимание, что хотя архивирование WAL позволяет сохранить любые изменения данных, произведённые в базе данных PostgreSQL, оно не затрагивает изменения, внесённые в конфигурационные файлы (такие как postgresql.conf
, pg_hba.conf
и pg_ident.conf
), поскольку эти изменения выполняются вручную, а не через SQL. Поэтому имеет смысл разместить конфигурационные файлы там, где они будут заархивированы обычными процедурами копирования файлов. Как перемещать конфигурационные файлы, рассказывается в Разделе 18.2.
Команда архивирования вызывается, только когда сегмент WAL заполнен до конца. Таким образом, если сервер постоянно генерирует небольшой трафик WAL (или есть продолжительные периоды, когда это происходит), между завершением транзакций и их безопасным сохранением в архиве может образоваться большая задержка. Чтобы ограничить время жизни неархивированных данных, можно установить archive_timeout, чтобы сервер переключался на новый файл сегмента WAL как минимум с заданной частотой. Заметьте, что неполные файлы, архивируемые досрочно из-за принудительного переключения по тайм-ауту, будут иметь тот же размер, что и заполненные файлы. Таким образом, устанавливать очень маленький archive_timeout
— неразумно; это приведёт к неэффективному заполнению архива. Обычно подходящее значение archive_timeout
— минута или около того.
Также вы можете принудительно переключить сегмент WAL вручную с помощью pg_switch_xlog
, если хотите, чтобы только что завершённая транзакция заархивировалась как можно скорее. Другие полезные функции, относящиеся к управлению WAL, перечисляются в Таблице 9.68.
Когда wal_level
имеет значение minimal
, некоторые команды SQL выполняются в обход журнала WAL, как описывается в Подразделе 14.4.7. Если архивирование или потоковая репликация были включены во время выполнения таких операторов, WAL не будет содержать информацию, необходимую для восстановления. (На восстановление после краха это не распространяется). Поэтому wal_level
можно изменить только при запуске сервера. Однако, чтобы изменить команду archive_command
, достаточно перезагрузить файл конфигурации. Если вы хотите на время остановить архивирование, это можно сделать, например, задав в качестве значения archive_command
пустую строку (''). В результате файлы WAL будут накапливаться в каталоге pg_xlog/
, пока не будет восстановлена действующая команда archive_command
.
24.3.2. Создание базовой резервной копии
Проще всего получить базовую резервную копию, используя программу pg_basebackup. Эта программа сохраняет базовую копию в виде обычных файлов или в архиве tar. Если гибкости pg_basebackup не хватает, вы также можете получить базовую резервную копию, используя низкоуровневый API (см. Подраздел 24.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, а этот процесс может быть долгим, если с момента последней базовой копии прошло много времени.
24.3.3. Создание базовой резервной копии через низкоуровневый API
Процедура создания базовой резервной копии с использованием низкоуровневого API содержит чуть больше шагов, чем метод pg_basebackup, но всё же относительно проста. Очень важно, чтобы эти шаги выполнялись по порядку, и следующий шаг выполнялся, только если предыдущий успешен.
Убедитесь, что архивирование WAL включено и работает.
Подключитесь к базе данных как суперпользователь и выполните команду:
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);
При этом контрольная точка будет выполнена как можно скорее.
Скопируйте файлы, используя любое удобное средство резервного копирования, например, tar или cpio (не pg_dump или pg_dumpall). В процессе копирования останавливать работу базы данных не требуется, это ничего не даёт.
Снова подключитесь к базе данных как суперпользователь и выполните команду:
SELECT pg_stop_backup();
Сервер выйдет из режима резервного копирования и автоматически переключится на следующий сегмент WAL. Это переключение выполняется для того, чтобы файл последнего сегмента WAL, записанного во время копирования, был готов к архивации.
После этого останется заархивировать файлы сегментов WAL, активных во время создания резервной копии, и процедура резервного копирования будет завершена. Функция
pg_stop_backup
возвращает указание на файл последнего сегмента, который требуется для формирования полного набора файлов резервной копии. Если включён режим архивации (параметрarchive_mode
), функцияpg_stop_backup
не завершится, пока не будет заархивирован последний сегмент. В этом случае файлы будут заархивированы автоматически, поскольку также должна быть настроена командаarchive_command
. Чаще всего это происходит быстро, но мы советуем наблюдать за системой архивации и проверять, не возникают ли задержки. Если архивирование остановится из-за ошибок команды архивации, попытки архивации будут продолжаться до успешного завершения, и только тогда резервное копирование окончится. Если вы хотите ограничить время выполненияpg_stop_backup
, установите соответствующее значение вstatement_timeout
.
Некоторые средства резервного копирования файлов выдают предупреждения или ошибки, если файлы, которые они пытаются скопировать, изменяются в процессе копирования. При получении базовой резервной копии активной базы данных это вполне нормально и не является ошибкой. Однако, вам нужно знать, как отличить ошибки такого рода от реальных ошибок. Например, некоторые версии 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_xlog/
. Эту небольшую корректировку стоит внести для снижения риска ошибок при восстановлении. Это легко организовать, если pg_xlog/
— символическая ссылка на каталог за пределами каталога данных (так часто делают из соображений производительности). Также имеет смысл исключить файлы postmaster.pid
и postmaster.opts
, содержащие информацию о работающем процессе postmaster (а не о том процессе postmaster, который будет восстанавливать эту копию). (Эти файлы могут ввести pg_ctl в заблуждение.)
Часто также стоит исключать из резервной копии каталог pg_replslot/
кластера, чтобы слоты репликации, существующие на главном сервере, не попадали в копию. В противном случае при последующем восстановлении копии на резервном сервере может получиться так, что он будет неограниченно долго сохранять файлы WAL, а главный не будет очищаться, если он следит за горячим резервом, так как клиенты этих слотов репликации будут продолжать подключаться и изменять состояние слотов на главном, а не резервном сервере. Даже если резервная копия предназначена только для создания нового главного сервера, копирование слотов репликации вряд ли принесёт пользу, так как к моменту включения в работу этого нового сервера содержимое этих слотов станет абсолютно неактуальным.
Также стоит отметить, что функция pg_start_backup
создаёт в каталоге кластера файлы backup_label
и tablespace_map
(затем они удаляются функцией pg_stop_backup
). Данные файлы, разумеется, заархивируются и войдут в состав резервной копии. В первый файл записывается метка, заданная при вызове pg_start_backup
, время запуска функции pg_start_backup
и имя начального файла WAL. Таким образом, в случае сомнений можно заглянуть внутрь архива резервной копии и точно определить, в каком сеансе резервного копирования он был создан. Файл карты табличных пространств содержит имена символических ссылок, как они существуют в каталоге pg_tblspc/
, и полный путь каждой символической ссылки. Эти файлы не только к вашему сведению; их существование и содержание важны для правильного проведения процесса восстановления системы.
Вы также можете создать резервную копию, когда сервер остановлен. В этом случае вы, очевидно, не сможете вызвать pg_start_backup
или pg_stop_backup
, и следовательно, вам надо будет самостоятельно как-то идентифицировать резервные копии и понимать, какие файлы WAL должны быть заархивированы. Поэтому обычно всё-таки лучше следовать вышеописанной процедуре непрерывного архивирования.
24.3.4. Восстановление непрерывной архивной копии
Допустим, худшее случилось, и вам необходимо восстановить базу данных из резервной копии. Порядок действий таков:
Остановите сервер баз данных, если он запущен.
Если у вас есть место для этого, скопируйте весь текущий каталог кластера баз данных и все табличные пространства во временный каталог на случай, если они вам понадобятся. Учтите, что эта мера предосторожности требует, чтобы свободного места на диске было достаточно для размещения двух копий существующих данных. Если места недостаточно, необходимо сохранить как минимум содержимое подкаталога
pg_xlog
каталога кластера, так как он может содержать журналы, не попавшие в архив перед остановкой системы.Удалите все существующие файлы и подкаталоги из каталога кластера и из корневых каталогов используемых табличных пространств.
Восстановите файлы базы данных из резервной копии файлов. Важно, чтобы у восстановленных файлов были правильные разрешения и правильный владелец (пользователь, запускающий сервер, а не
root
!). Если вы используете табличные пространства, убедитесь также, что символьные ссылки вpg_tblspc/
восстановились корректно.Удалите все файлы из
pg_xlog/
; они восстановились из резервной копии файлов и поэтому, скорее всего, будут старее текущих. Если вы вовсе не архивировалиpg_xlog/
, создайте этот каталог с правильными правами доступа, но если это была символьная ссылка, восстановите её.Если на шаге 2 вы сохранили незаархивированные файлы с сегментами WAL, скопируйте их в
pg_xlog/
. (Лучше всего именно копировать, а не перемещать их, чтобы у вас остались неизменённые файлы на случай, если возникнет проблема и всё придётся начинать сначала.)Создайте командный файл восстановления
recovery.conf
в каталоге кластера баз данных (см. Главу 26). Вы можете также временно изменитьpg_hba.conf
, чтобы обычные пользователи не могли подключаться, пока вы не будете уверены, что восстановление завершилось успешно.Запустите сервер. Сервер запустится в режиме восстановления и начнёт считывать необходимые ему архивные файлы WAL. Если восстановление будет прервано из-за внешней ошибки, сервер можно просто перезапустить и он продолжит восстановление. По завершении процесса восстановления сервер переименует файл
recovery.conf
вrecovery.done
(чтобы предотвратить повторный запуск режима восстановления), а затем перейдёт к обычной работе с базой данных.Просмотрите содержимое базы данных, чтобы убедиться, что вы вернули её к желаемому состоянию. Если это не так, вернитесь к шагу 1. Если всё хорошо, разрешите пользователям подключаться к серверу, восстановив обычный файл
pg_hba.conf
.
Ключевой момент этой процедуры заключается в создании файла конфигурации восстановления, описывающего, как будет выполняться восстановление и до какой точки. В качестве прототипа вы можете использовать файл recovery.conf.sample
(он обычно помещается в каталог share/
после установки). Единственное, что совершенно необходимо указать в recovery.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_xlog/
; благодаря этому можно использовать последние незаархивированные сегменты. Однако файлы в pg_xlog/
будут менее предпочтительными, если такие сегменты окажутся в архиве.
Обычно при восстановлении обрабатываются все доступные сегменты WAL и, таким образом, база данных восстанавливается до последнего момента времени (или максимально близкого к нему, в зависимости от наличия сегментов WAL). Таким образом, восстановление обычно завершается с сообщением «файл не найден»; точный текст сообщения об ошибке зависит от того, что делает restore_command
. Вы также можете увидеть сообщение об ошибке в начале восстановления для файла с именем типа 00000001.history
. Это также нормально и обычно не говорит о какой-либо проблеме при восстановлении в простых ситуациях; подробнее об этом рассказывается в Подразделе 24.3.5.
Если вы хотите восстановить базу на какой-то момент времени (скажем, до момента, когда неопытный администратор базы данных удалил основную таблицу транзакций), просто укажите требуемую точку остановки в recovery.conf
. Вы можете задать эту точку, иначе называемую «целью восстановления», по дате/времени, именованной точке восстановления или определённому идентификатору транзакции. На момент написания этой документации полезными могут быть только указания даты/времени или имени точки восстановления, пока нет никаких средств, позволяющих точно определить, какой идентификатор транзакции нужно выбрать.
Примечание
Точка останова должна указывать на момент после окончания базового копирования, т. е. после времени завершения pg_stop_backup
. Использовать базовую резервную копию для восстановления на момент времени, когда она ещё только создавалась, нельзя. (Чтобы восстановить данные на этот момент времени, придётся вернуться к предыдущей базовой резервной копии и накатывать изменения с этой позиции.)
Если при восстановлении обнаруживаются повреждённые данные WAL, восстановление прерывается в этом месте и сервер не запускается. В этом случае процесс восстановления можно перезапустить с начала, указав «цель восстановления» до точки повреждения, чтобы восстановление могло завершиться нормально. Если восстановление завершается ошибкой из-за внешней причины, например, из-за краха системы или недоступности архива WAL, его можно просто перезапустить, и оно продолжится с того места, где было прервано. Перезапуск восстановления реализован по тому же принципу, что и контрольные точки при обычной работе: сервер периодически сохраняет всё текущее состояние на диске и отражает это в файле pg_control
, чтобы уже обработанные данные WAL не приходилось сканировать снова.
24.3.5. Линии времени
Возможность восстановить базу данных на некий предыдущий момент времени создаёт некоторые сложности, сродни научно-фантастическим историям о путешествиях во времени и параллельных мирах. Например, предположим, что в начальной истории базы данных вы удалили важную таблицу в 17:15 во вторник, но осознали эту ошибку только в среду в полдень. Вы можете спокойно взять резервную копию, восстановить данные на 17:14 во вторник и запустить сервер. В этой истории мира базы данных вы никогда не удаляли вышеупомянутую таблицу. Но предположим, что позже вы заметили, что это была не такая уж хорошая идея и захотели вернуться к утру среды в первоначальной истории базы данных. Вы не сможете сделать это, если в процессе работы базы данных она успеет перезаписать какие-либо файлы-сегменты WAL, приводящие к моменту времени, к которому вы хотите вернуться теперь. Таким образом, для получения желаемого результата необходимо как-то отличать последовательности записей WAL, добавленные после восстановления на какой-то момент времени от тех, что существовали в начальной истории базы данных.
Для решения этой проблемы в PostgreSQL есть такое понятие, как линия времени. Всякий раз, когда завершается восстановление из архива, создаётся новая линия времени, позволяющая идентифицировать последовательность записей WAL, добавленных после этого восстановления. Номер линии времени включается в имя файлов-сегментов WAL, так что файлы новой линии времени не перезаписывают файлы WAL, сгенерированные предыдущими линиями времени. Фактически это позволяет архивировать много различных линий времени. Хотя это может показаться бесполезной возможностью, на самом деле она часто бывает спасительной. Представьте, что вы не определились, какую точку времени выбрать для восстановления, и таким образом должны проводить восстановление методом проб и ошибок, пока не найдёте лучший момент для ответвления от старой истории. Без линий времени этот процесс быстро стал бы очень запутанным. А благодаря линиям времени, вы можете вернуться к любому предыдущему состоянию, включая состояния в ветках линий времени, покинутых ранее.
Каждый раз, когда образуется новая линия времени, PostgreSQL создаёт файл «истории линии времени», показывающий, от какой линии времени ответвилась данная и когда. Эти файлы истории нужны, чтобы система могла выбрать правильные файлы-сегменты WAL при восстановлении из архива, содержащего несколько линий времени. Таким образом, они помещаются в область архивов WAL так же, как и файлы сегментов WAL. Файлы истории представляют собой небольшие текстовые файлы, так что они не занимают много места и их вполне можно сохранять неограниченно долго (в отличие от файлов сегментов, имеющих большой размер). Если хотите, вы можете добавлять в файл истории комментарии, свои собственные заметки о том, как и почему была создана эта конкретная линия времени. Такие комментарии будут особенно ценны, если в результате экспериментов у вас образуется хитросплетение разных линий времени.
По умолчанию при восстановлении восстанавливается та же линия времени, которая была текущей при создании базовой резервной копии. Если вы хотите восстановить состояние на какой-либо дочерней линии времени, (то есть, хотите вернуться к некоторому состоянию, которое тоже было получено в результате попытки восстановления), вам необходимо указать идентификатор целевой линии времени в recovery.conf
. Восстановить состояние в линии времени, ответвившейся раньше, чем была сделана базовая резервная копия, нельзя.
24.3.6. Советы и примеры
Ниже мы дадим несколько советов по настройке непрерывного архивирования.
24.3.6.1. Обособленные горячие резервные копии
Средства резервного копирования PostgreSQL можно применять для создания обособленных горячих копий. Эти копии нельзя использовать для восстановления на момент времени, но создаются и восстанавливаются они обычно гораздо быстрее, чем дампы pg_dump. (Они также намного больше, чем дампы pg_dump, так что в некоторых случаях выигрыш в скорости может быть потерян.)
Как и базовые резервные копии, обособленную горячую копию проще всего получить, используя программу pg_basebackup. Если вы вызовете эту программу с параметром -X
, в эту копию автоматически будет включён весь журнал транзакций, необходимый для её использования, так что никакие особые действия для восстановления не потребуются.
Если нужна дополнительная гибкость в процессе копирования файлов, создавать обособленные горячие копии можно также на более низком уровне. Чтобы подготовиться к получению такой копии на низком уровне, установите в wal_level
уровень archive
(или выше), в 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
, а в противном случае просто вернёт нулевой код возврата (и тогда PostgreSQL сможет переработать ненужный файл 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. Пожалуйста, не забудьте добавить в ваши скрипты резервного копирования обработку ошибок.
24.3.6.2. Сжатие журналов в архиве
Если размер архива имеет большое значение, можно воспользоваться gzip и сжимать архивные файлы:
archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f'
При этом для восстановления придётся использовать gunzip:
restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'
24.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, будут записываться в журнал сервера баз данных, что позволит легко диагностировать ошибки в сложных конфигурациях.
24.3.7. Ограничения
На момент написания документации методика непрерывного архивирования имеет несколько ограничений. Они могут быть ликвидированы в будущих версиях:
Операции с хеш-индексами в настоящее время не проходят через WAL, так что при воспроизведении WAL эти индексы не меняются. Это означает, что добавление данных будет игнорироваться индексом, изменяемые строки будут исчезать, а на удалённые строки сохранятся ссылки. Другими словами, если вы изменяете таблицу, для которой есть хеш-индекс, на ведомом сервере вы будете получать некорректные результаты запросов. Поэтому по завершении восстановления рекомендуется вручную выполнить REINDEX для каждого такого индекса.
Если во время создания базовой резервной копии выполняется команда CREATE DATABASE, а затем база-шаблон, задействованная в
CREATE DATABASE
, изменяется, пока продолжается копирование, возможно, что при восстановлении эти изменения распространятся также и на созданную базу данных. Конечно, это нежелательно. Во избежание подобных рисков, лучше всего не изменять никакие базы-шаблоны во время получения базовой резервной копии.Команды CREATE TABLESPACE записываются в WAL с абсолютным путём и, таким образом, при воспроизведении WAL будут выполнены с тем же абсолютным путём. Это может быть нежелательно, если журнал воспроизводится на другой машине. Но опасность есть, даже если журнал воспроизводится на той же машине, но в другом каталоге данных: при воспроизведении будет так же перезаписано содержимое исходных табличных пространств. Чтобы избежать потенциальных проблем такого рода, лучше всего делать новую базовую резервную копию после создания или удаления табличных пространств.
Также следует заметить, что стандартный формат WAL не очень компактный, так как включает много снимков дисковых страниц. Эти снимки страниц предназначены для поддержки восстановления после сбоя, на случай, если понадобится исправить страницы, записанные на диск частично. В зависимости от аппаратного и программного обеспечения вашей системы, риск частичной записи может быть достаточно мал, так что его можно игнорировать, и в этом случае можно существенно уменьшить общий объём архивируемых журналов, выключив снимки страниц с помощью параметра full_page_writes. (Прежде чем делать это, прочтите замечания и предупреждения в Главе 29.) Выключение снимков страниц не препятствует использованию журналов для восстановления PITR. Одним из направлений разработки в будущем является сжатие архивируемых данных WAL, путём удаления ненужных копий страниц даже при включённом режиме full_page_writes
. Тем временем администраторы могут сократить количество снимков страниц, включаемых в WAL, увеличив параметры интервала контрольных точек в разумных пределах.