Хранилище БД

Все БД кластера хранятся в одной директории:

/var/lib/postgresql/<major_instance_version>/<cluster_name>

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

Заранее вас предупреждаю: не трогайте просто так файлы в этой директории. Даже не пытайтесь изменять их права и содержимое без веской на то причины.

Файлов и вложенных директорий много, отмечу лишь необходимые нам:

PG_VERSION – это файл, в котором записана мажорная версия кластера. Смотреть можно, трогать нельзя.

pg_wal – директория, которая содержит записи WAL.

base – директория, которая хранит в себе все БД кластера. В ней находятся поддиректории для каждой из БД кластера. Даже не пытайтесь понять ее содержимое. С ней обычно работают разработчики.

Есть три основные вещи, которые всегда необходимо иметь ввиду:

  • В хранилище БД всегда должно быть достаточно свободного пространства. Если место закончится – PostgreSQL упадет в PANIC и выключится. Самое страшное для PostgreSQL – это именно отсутствие свободного места. Сервер может пережить отсутствие ресурсов CPU и RAM, но только не отсутствие места для баз.

  • Права на директорию кластера …/<cluster_name> должны быть 700. Именно у директории, название которой является именем кластера (а также всех вложенных в нее файлов и директорий).

  • Владельцем директории и всех вложенных директорий и файлов должен быть пользователь postgres или любой другой суперпользователь PostgreSQL. Главное, чтобы под ним был запущен процесс postgresql и все подпроцессы. Но я вам все равно не рекомендую пытаться его изменить. Для новичков это будет непонятно, и они наделают ошибок. Так что просто оставляйте владельцем кластеров роль postgres, все равно она ничем нам не помешает.

Про выбор файловой системы (ФС) для хранения БД: только XFS или ext4, если вам не нужны особенности ZFS, BTRFS (по типу снапшотов). Почему так – смотрите по ссылке внизу. ZFS и BTRFS отличные ФС, и на них тоже можно разместить БД, правда производительность будет ниже. Не существует универсальной ФС, которая была бы хороша везде. Разумеется, вы должны хорошо знать устройство каждой их них. В малых системах разница, конечно, будет минимальная.

https://www.enterprisedb.com/blog/postgres-vs-file-systems-performance-comparison

Что касается выбора ФС для самой ОС: можете использовать что угодно. К примеру, для обеспечения отказоустойчивости развернуть систему на нескольких физических дисках на ZFS или BTRFS. Если вы знакомы с LVM – то можете использовать и его с обычной ext4. Самое главное, чтобы потом вы смогли систему восстановить. Файловая система ОС и файловая система для хранилища БД не обязательно должны быть одинаковыми.

И последнее: физическое оборудование. Если у вас базы на HDD, если вы диски не проверяете, то никакие файловые системы вам не помогут. Также не забывайте об отказоустойчивости дисковой подсистемы вне зависимости от того, делаете вы бэкапы или нет. Потому что восстановление все равно обойдется дороже.

Хранилище может быть как локальное, так и сетевое. В этом нет никаких проблем. Главное, подмонтируйте его так, как надо, чтобы оно не слетело после перезапуска ОС.

Миграция (перемещение) БД

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

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

Также под миграцией может подразумеваться процесс изменения самой СУБД, если приложение поддерживает обе из них.

Логический бэкап

Утилита pg_dump представлена двумя командами: pg_dump и pg_dumpall.

Все, что вам необходимо знать о pg_dump: она годится только для миграции баз, ни в коем случае не используйте ее в качестве инструмента резервного копирования. Все из-за ограничений, таких как: отсутствие согласованности транзакций (бэкап начался в 14:00, закончился в 16:00, и изменения в период с 14:00-16:00 не будут сохранены); неэффективное использование хранилища (pg_dump создает бэкапы в текстовом формате, а не в двоичном, это увеличивает объем дампа и время восстановления, даже с учетом сжатия). Дамп это просто выгрузка в файл SQL (то есть последовательность команд, при выполнении которой будут созданы новые базы с той же информацией).

Единственное нормальное применение утилиты pg_dump – это миграция. Под миграцией подразумевается в принципе любое перемещение: в другую директорию, на другой хост, на более новую или старую мажорную версию и т.п. Возможно, утилита будет полезна разработчикам, которые работают с отдельными объектами кластера.

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

С синтаксисом команды pg_dump прошу ознакомиться в документации:

https://www.postgresql.org/docs/current/app-pgdump.html

Нас интересуют только несколько параметров:

-d – задает имя БД.

Утилита может работать только с одной конкретной базой. Все остальные объекты кластера скопированы не будут. Иными словами, для перемещения всего кластера команда pg_dump не подходит, даже если в кластере только одна база.

-j – задает количество процессов.

Чем их больше – тем быстрее создается дамп. Главное, не назначайте более 1 процесса на горячей (продуктивной) системе, а то получите дедлоки (deadlock – это взаимная блокировка транзакций). Этот параметр поможет быстрее выгрузить дамп холодной (неактивной) базы.

-f <file> – задает абсолютный путь файла, в который будет выгружен дамп.

Владелец базы (в нашем случае postgres) должен иметь доступ к этому файлу. Если же файла нет, то к директории, чтобы этот файл создать.

Есть еще второй параметр -F, который позволяет изменить тип этого файла. По умолчанию скриптовый sql-файл (plain), но я бы рекомендовал использовать tar (который, правда, придется сжимать отдельно).

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

-Z – задает алгоритм сжатия.

Всегда задавайте, потому что это must have, best practice и т.д. Какой алгоритм выбрать – zstd без уровня сжатия (он самый лучший, а сжатие бессмысленно). Потому что если мы не зададим ничего, то будет использован алгоритм gzip с 6 уровнем сжатия. Алгоритм сжатия задается с уровнем сжатия через двоеточие. В общем, задавайте только алгоритм zstd без уровня сжатия (0), либо назначайте хотя бы минимальный уровень (1-3). Сжимать можно сколько угодно, но всегда есть черта, после которой объем архива не уменьшается, а время сжатия увеличивается в десятки раз.

Ниже в таблице приведено сравнение скорости создания дампа командой pg_dump с различными алгоритмами и уровнями сжатия, а также их объем для тестовой базы весом 1503 MB (10 млн. кортежей). Тестирование проводилось при помощи time в pgbench на SSD NVMe PCI-E 4.0.

Для алгоритма gzip:

Уровень сжатия

real

user

sys

Объем (MB)

0 (не gz:6, без сжатия)

0m4.044s

0m0.642s

0m1.944s

933

1

0m3.486s

0m2.016s

0m0.629s

28

2

0m3.468s

0m2.036s

0m0.595s

28

5

0m4.137s

0m3.735s

0m0.359s

27

9

0m5.659s

0m5.265s

0m0.347s

27

Для алгоритма lz4:

Уровень сжатия

real

user

sys

Объем (MB)

0

0m4.661s

0m1.187s

0m1.103s

50

1

0m4.393s

0m1.211s

0m1.070s

50

3

0m3.825s

0m3.366s

0m0.419s

48

5

0m4.564s

0m4.070s

0m0.454s

48

9

0m5.494s

0m4.976s

0m0.457s

48

12

0m57.881s

0m57.036s

0m0.639s

47

Для алгоритма zstd:

Уровень сжатия

real

user

sys

Объем (MB)

0

0m4.113s

0m1.556s

0m0.851s

8

1

0m4.373s

0m1.311s

0m0.978s

8

3

0m4.088s

0m1.544s

0m0.866s

8

5

0m4.268s

0m1.337s

0m0.930s

9

9

0m9.213s

0m7.556s

0m0.752s

8

12

0m17.450s

0m15.347s

0m0.955s

8

15

0m8.101s

0m6.513s

0m0.764s

8

18

0m34.789s

0m32.705s

0m0.892s

6

22

36m57.544s

36m58.072s

0m1.258s

7

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

Также обратите внимание, что тестовая база была объемом 1503 MB, а ее дамп без сжатия всего лишь 933 MB. Это норма, так и должно быть. Не буду лезть в дебри об устройстве кластера и баз, просто имейте ввиду, что дамп баз может быть как больше, так и меньше исходного объема.

В общем, всегда либо используйте алгоритм сжатия zstd для pg_dump. Это самое важное, что я хотел до вас донести.

Разумеется, проводить замеры на тестовой «базе» объемом в 1503 мегабайта это такое себе, и в продуктивной среде на нормальных базах объемом от 100 GB все будет отличаться. Однако вы уже знаете, от чего отталкиваться и что иметь ввиду.

Например, от имени владельца БД создадим дамп базы dbtest1, которая расположена на локальном хосте на экземпляре, который прослушивает 5440 порт, сожмем его по алгоритму zstd без уровня сжатия, и выгрузим его в обычный sql-файл, полный путь которого /mnt/backup/dump_dbtest1.sql (суффикс .sql нужен просто для того, чтобы потом было легче идентифицировать файл дампа, его писать не обязательно, файл остается файлом).

pg_dump -p 5440 -Z zstd:0 -f /mnt/backup/dump_dbtest1.sql -d dbtest1

pg_dumpall это такая же команда, как и pg_dump, с той лишь разницей, что она бэкапит весь кластер целиком (все базы, а еще роли, табличные пространства и т.п.). Но основной проблемой все же является отсутствие сжатия (то есть команда работает только в формате plain). Выше вы уже видели, насколько сжатие может быть полезно. В целом, делает все то же, что и pg_basebackup, но вот отсутствие возможности сжатия создает очень много проблем. Раньше до появления pg_basebackup для создания резервных копий использовали pg_dumpall, либо придумывали связки с pg_dump, чтобы сэкономить место, но в основном работали со сторонними продуктами.

В общем, используйте утилиту pg_dump, если вы хотите по-быстрому провести миграцию одной базы (если мало времени и дискового пространства) или всего экземпляра небольшого объема (если есть и время, и свободное место, а требуется просто перенести экземпляр целиком). Не используйте ее для создания резервных копий (pgdump/pg_dumpall), для этого существует утилита pg_basebackup.

Физический бэкап

Речь пойдет именно про 15-16 версии. В 15 версии pg_basebackup существенно доработали и добавили несколько полезных параметров, которых не было в 14 версии.

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

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

Также учтите, что утилита pg_basebackup бэкапит только данные. Так что конфигурационные файлы копируйте отдельно (а лучше просто заново поменять все параметры на новом конфиге). Собственно, утилита pg_dump также не копирует конфиги.

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

https://www.postgresql.org/docs/16/app-pgbasebackup.html

Синтаксис команды тот же, что и у pg_dump, однако параметры уже отличаются.

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

-t – похож на -D, но с тем отличием, что позволяет, к примеру, создать резервную копию на удаленном сервере PostgreSQL и отправить на локальный. Смысла в этом параметре немного, просто отметил, чтобы вы не путали его с -D.

-F – итоговый формат резервной копии. Можем скопировать кластер как есть, а можем и в формате tar. Это очень удобно, тем более что сжатие по zstd работает только с итоговым форматом tar (в остальных случаях автоматически используется сжатие по алгоритму zlib, который в 10 раз медленнее, чем zstd). Поэтому задаем значение t.

--waldir=<wal_directory> – задает директорию, в которую отдельно будут скопированы файлы WAL. Опять же отметил, чтобы вы не путали, а то еще укажите исходное расположение WAL.

-X – самый полезный параметр. Позволяет во время создания бэкапа сохранять еще и изменения WAL. К примеру, если у нас бэкап начался в 14:00, а закончился в 16:00, то все изменения на момент 16:00 также будут записаны. Но для достижения этого необходимо выставить значение s (stream). Это значение по умолчанию, правда лучше задать явно на всякий случай. Также имейте ввиду, что при создании бэкапа в виде tar (см. параметр -F), все изменения WAL будут записаны в отдельный архив pg_wal.tar. Это не плохо, просто немного затрудняет восстановление.

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

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

1. Начало резервного копирования (запуск pg_basebackup).

2. Система ждет, когда начнет создаваться первичный чекпоинт (это я его так обозвал, чтобы выделить, потому что у него нет имени, по сути дела это такой же чекпоинт).

3. Начало создания первичного чекпоинта.

4. Процесс создания первичного чекпоинта.

5. Первичный чекпоинт создан.

На 6-8 и 9-11 также показано два процесса создания чекпоинта. Они создаются с разной периодичностью в зависимости от того, сколько записей накопилось в WAL. Время их создания также может отличаться, в зависимости от нагрузки системы. Вторичные чекпоинты, которые могут быть созданы как автоматически, так и вручную, никак не мешают нам при копировании, поскольку система использует слот репликации. Без слота репликации эти чекпоинты удалили бы необходимые файлы WAL.

12. Резервная копия завершена.

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

Также учтите, что при использовании этого параметра вам необходимы права REPLICATION или SUPERUSER, а также разрешение в pg_hba.conf (вместо имени БД указывайте replication).

Значение параметра max_wal_senders в postgresql.conf должно быть 2 или больше (один для данных, второй для WAL). По умолчанию стоит 10, и этого более чем достаточно, но лучше проверить.

-z – сжимает бэкап по алгоритму gzip с 6 уровнем сжатия, если у нас резервная копия является архивом tar. Смысла в этом нет никакого. Поэтому не задавайте.

-Z – изменено в 15 версии. Так же, как и в pg_dump, задает алгоритм и уровень сжатия. С тем лишь отличием, что мы можем указать, где будет выполняться сжатие: на клиенте или на сервере (выставляйте на сервере, потому что бэкапы без сжатия весят очень много, а 10 Gb сеть есть не у каждого). Напоминаю, что сжатие работает только, если бэкап создается в виде архива tar.

Ниже показан объем итоговых резервных копий и время их создания той же тестовой базы на 10 млн. кортежей весом 1503 MB. В этот раз время создания бэкапа приведено примерно.

В таблицах ниже приведено время создания физического бэкапа в зависимости от алгоритма и уровня сжатия (приведены не все, я просто выбрал несколько промежуточных). Учтите только, что все значения времени измерялись с погрешностью в 1 секунду. Исходный объем тот же: 1,5 GB. Напоминаю, что каждый из алгоритмов имеет разный уровень сжатия:

gzip: 1-9

lz4: 0-12

zstd: 0-22

Для алгоритма gzip:

Уровень сжатия

Итоговый размер (MB)

Время создания (sec)

1

91

5

3

89

6

5

84

9

9

84

80

Для алгоритма lz4:

Уровень сжатия

Итоговый размер (MB)

Время создания (sec)

0

185

2

1

185

2

3

167

5

5

166

6

9

166

13

12

150

208

Для алгоритма zstd:

Уровень сжатия

Итоговый размер (MB)

Время создания (sec)

0

84

3

1

82

3

3

84

4

5

80

5

9

75

13

12

59

27

15

59

49

18

50

372

Вывод в общем простой: всегда используйте алгоритм zstd. Его также можно использовать более высокими уровнями сжатия, если места на диске ну вот вообще нет, а время создания не так важно. Очевидно также, что вы можете вручную (отдельной утилитой zstd, которая, в отличие от lz4, уже предустановлена в вашем дистрибутиве) сжать резервную копию еще раз после ее создания. Так даже выйдет гораздо быстрее, чем просто задавать один раз высокий уровень сжатия. Учтите также, что эффективность алгоритма зависит от того, какие данные ему придется сжимать. Иными словами, все опять же зависит от ваших баз, поэтому значения могут отличаться. Если вы периодически делаете физические бэкапы продуктивной системы – просто ради интереса попробуйте ее бэкап сжать алгоритмом zstd с различными уровнями. Все познается в сравнении.

По-хорошему, также необходимо было мониторить потребление ресурсов в зависимости от используемого алгоритма. Однако это сложно, долго и дорого, тем более что у каждого HW разное. Вам и так всегда придется чем-то жертвовать. Уменьшение занимаемого дискового пространство того стоит.

-c – еще один важный параметр. Вы уже знакомы с чекпоинтами. Проблема в том, что после запуска pg_basebackup СУБД должна создать чекпоинт, чтобы начать создание резервной копии (потому что, грубо говоря, файлы WAL, которые были созданы до начала создания бэкапа, не будут скопированы; копируются именно те файлы WAL, которые появились после того, как начал записываться чекпоинт, который также должен быть создан после запуска pg_basebackup). Поэтому мы можем приказать серверу сразу создать чекпоинт после запуска pg_basebackup, задав значение fast. Но есть одна «маленькая» проблема: по умолчанию стоит значение spread. То есть система будет ждать, пока чекпоинт не будет создан автоматически или вручную (привет любителям выставлять периодичность чекпоинта в 1 день). Все последующие чекпоинты будут записаны без проблем, а данные также будут сохранены. Как вы понимаете, значение по умолчанию – это просто потеря времени. Выше мы уже разбирали периодичность чекпоинтов в разделе про WAL. Всегда выставляйте значение fast. Также учтите, что во время копирования через pg_basebackup с потоковой репликацией значение параметра full_page_writes будет выставлено в on (на случай, если вы мониторите изменения параметров). Единственный минус такого подхода заключается в том, что фактически мы вызываем чекпоинт напрямую. А это значит, что процесс записи файлов журнала в БД имеет наивысший приоритет, то есть у вас на какой-то период времени загрузка на дисковую подсистему будет максимальной. Имейте это ввиду, особенно если у вас базы на HDD или объем журнала превышает 1 GB.

-l <label> – задает описание резервной копии в кавычках. Полезно, если бэкапов несколько. Само описание находится в файле backup_label в строке LABEL. Обязательно помечайте ОС, ее версию, а также версию кластера (для вас). В том же файле находится другая полезная информация, к примеру, время начала процесса создания бэкапа.

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

-N – не путать с -n и ни в коем случае не использовать.

-P – задавайте, если вы хотите в консоли видеть прогресс выполнения в процентах. Этот параметр ничего не стоит и не потребляет ресурсы. Учтите только, что при потоковой репликации WAL (см. параметр -X) может выводится неверное значение. К примеру, 99% может висеть на протяжении 15 минут (реальный случай).

-v – тоже полезен, рекомендую включить. Так в случае затупа системы вы поймете, на каком этапе процесс встал.

-r <rate> – тоже полезен, особенно когда бэкапим продуктивную систему. Позволяет ограничить максимальную скорость передачи данных. Какое значение задавать – зависит от ваших нагрузок. Если вам резервная копия необходима срочно, то не ограничивайте. Если же вы делаете бэкап на продуктивной системе, то необходимо ограничить в зависимости от нагрузки на дисковую подсистему (казалось бы, примитивная вещь, но многие ею пренебрегают).

Отдельно про слоты репликации

По умолчанию всегда создается временный (новый) слот репликации. Мы можем создать новый слот репликации или назначить процесс создания бэкапа к уже существующему слоту.

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

Основная задача слотов репликации – сохранение передаваемой информации в случае временного отсутствия доступа к серверу, на который мы передаем данные. В основном, это касается потоковой репликации WAL (потому что они могут добавляться в процессе копирования основной информации). Если связь прервалась, файлы WAL будут записываться на исходном сервере-отправителе, который будет ждать, когда сервер-получатель станет доступен. Как только данные по протоколу слота репликации достигнут целевого сервера, он отправит ответ, какие данные он получил, а какие нет. Если же вдруг места для WAL не хватило – то ничего страшного (потому что параметр max_wal_size является гибким, а мы также выставили его с запасом). Страшно будет, когда свободное место в файловой системе закончится.

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

Напомню еще раз: по умолчанию при запуске pg_basebackup всегда создается временный слот репликации. Вы можете создать слот вручную, однако полезен он будет только для непрерывной репликации. Для создания бэкапа временного слота хватит с головой. Самое главное – не отключайте временный слот репликации. За отключение временного слота отвечает параметр --no-slot.

-C – создает новый слот.

-S – задает имя слота, который будет использоваться для бэкапа. Напоминаю, что слоты используются для обеспечения беспрерывной передачи WAL, поэтому необходимо также значение -Xs. Можно подключиться как к существующему слоту, так и к созданному при помощи параметра -C.

Восстановление физической резервной копии

Мало иметь на руках резервную копию, необходимо также научится ее восстанавливать. Время восстановления гораздо важнее, чем время создания резервной копии. Восстановление копии, созданной при помощи pg_basebackup, возможно только на той же мажорной версии PostgreSQL (минорная версия может отличаться), и на той же ОС той же версии. Если для вас недопустим простой сервера – то смотрите в сторону кластеризации, или обеспечивайте высокую доступность другими методами (к примеру, сервер СУБД может быть VM на HA кластере гипервизора).

Резервная копия, созданная через pg_basebackup, это просто директория или архив с данными. Если это сжатый архив, то его придется распаковать. Распаковка также занимает время.

В таблице ниже показано время распаковки архивов base (без pg_wal), сжатых по алгоритмам zstd, lz4 и gzip с различными уровнями сжатия. Время измерялось при помощи time. Объем архива после распаковки во всех случаях одинаков: 1519 MB (чуть выше исходного, это норма).

Для алгоритма zstd:

Уровень сжатия

Объем сжатого архива (MB)

Время time

real

user

sys

0

67

0m1.156s

0m0.660s

0m0.509s

1

65

0m1.190s

0m0.614s

0m0.495s

3

67

0m1.274s

0m0.656s

0m0.512s

5

64

0m1.059s

0m0.634s

0m0.517s

9

59

0m1.143s

0m0.660s

0m0.519s

Для алгоритма lz4:

Уровень сжатия

Объем сжатого архива (MB)

Время time

real

user

sys

0

168

0m0.896s

0m0.229s

0m0.366s

1

168

0m2.016s

0m0.236s

0m0.375s

3

151

0m1.409s

0m0.233s

0m0.361s

5

150

0m0.858s

0m0.237s

0m0.376s

9

150

0m1.199s

0m0.232s

0m0.377s

Для алгоритма gzip:

Уровень сжатия

Объем сжатого архива (MB)

Время time

real

user

sys

1

91

0m4.139s

0m3.809s

0m0.324s

3

89

0m4.127s

0m3.778s

0m0.327s

5

84

0m4.100s

0m3.762s

0m0.320s

9

84

0m4.080s

0m3.722s

0m0.348s

Как видно из таблиц выше, gzip это позор, и он по скорости распаковки также проигрывает в ноль. Возможно, в будущем PostgreSQL откажется от gzip в роли алгоритма по умолчанию.

В целом, для каждого из алгоритмов скорость распаковки не так сильно зависит от уровня сжатия (но это из-за малого объема, на больших архивах разница между разными уровнями сжатия при распаковке будет заметнее). Это и так понятно для тех, кто читал теорию. Но проверить еще раз никогда не повредит. Не забудьте только распаковать его столько раз, сколько вы его сжимали, потому что бывает лучше сжать дважды 1-3 уровнями, чем один раз 15.

Учтите также, что объем архива, время его создания и распаковки всегда будет разным, даже если условия одни и те же. Я мог бы сделать по 3-5 и более измерений для каждого из значений, а потом просто взять среднее. Однако на это уйдет целая вечность. Все равно вам самим придется собирать данные конкретно для вашей среды.

Коротко говоря, никаких проблем со скоростью распаковки физического бэкапа не возникнет. Все зависит только от объема и типа исходных данных, а также самого алгоритма. Далее смотрите именно на скорость создания бэкапа и его объем. Очевидно также, что все зависит от наличия ресурсов CPU и RAM, которые задействуются при архивации, а также от производительности дисковой подсистемы и пропускной способности сети (в зависимости от того, откуда и куда восстанавливается резервная копия).

Перед восстановлением имейте ввиду, что восстановление физического бэкапа на другую ОС невозможно. У вас должна быть ровно та же самая операционная система, той же самой мажорной версии. СУБД это тоже касается (но только в плане мажорной версии).

Восстановление проходит в несколько этапов:

  • Определяем две директории: где хранится бэкап, и куда его необходимо восстановить.

  • Останавливаем именно тот экземпляр, на который будем восстанавливать бэкап (саму СУБД полностью останавливать не обязательно).

  • Удаляем все файлы из директории, в которую планируется восстановление (то есть версию и имя кластера не трогаем, например: …/16/cluster1/ ). Имя в директории целевого кластера не обязательно должно быть таким же, как у кластера, резервную копию которого мы создавали.

  • Копируем/распаковываем (в зависимости от того, в каком виде у вас хранится резервная копия) сам бэкап в целевую директорию при помощи rsync, tar, zstd, lz4 – в зависимости от того, как и чем бэкап был создан.

  • При наличии архива pg_wal.tar также распаковываем/копируем его содержимое в директорию pg_wal.

  • Проверяем, что директория кластера принадлежит пользователю postgres.

  • Запускаем экземпляр, проверяем все конфиги и логи, пробуем подключиться.

Пример восстановления физической резервной копии

Исходные данные:

Директория, в которой находится бэкап

/mnt/backup

Файлы в этой директории

backup_manifest, base.tar.zst, pg_wal.tar

Версия кластера, на который планируется восстановление

16

Имя кластера, на который планируется восстановление

cluster2

Директория, в которую планируется восстановление

/var/lib/postgresql/16/cluster2

Порядок действий

1) Останавливаем экземпляр:

sudo systemctl stop postgresql@16-cluster2

2) Удаляем, переименовываем или перемещаем файлы из целевой директории (в моем примере я просто их удалю):

rm -r /var/lib/postgresql/16/cluster2/*

3) Перемещаем base.tar.zst в целевую директорию (имя исходного экземпляра для нас не важно, важны только данные кластера):

Перемещаем сразу, потому что это сжатый архив, и он мало весит.

4) Распаковываем перемещенный архив:

zstd -d --rm /var/lib/postgresql/16/cluster2/base.tar.zst
tar -x -f /var/lib/postgresql/16/cluster2/base.tar

Имейте ввиду, что необходимо в 2-3 раза больше места, чем занимает сам архив после декомпрессии. Иначе распаковывайте его в хранилище БД, а только затем копируйте. Все зависит от того, где у вас есть место на диске. Почему мы тогда просто не использовали формат plain? Потому что бэкапы мы делаем гораздо чаще, чем их восстанавливаем, а сама резервная копия в сжатом виде может располагаться где-то далеко, и копировать 200 GB по 10Gb сети это просто потеря трех минут на ровном месте (если дисковая подсистема позволяет).

5) Также восстанавливаем WAL (если он был скопирован):

tar -x -f /mnt/backup/pg_wal.tar -C /var/lib/postgresql/16/cluster2/pg_wal

6) Убеждаемся, что путь до файлов БД и других конфигов в postgresql.conf являются верными. Если нет, то меняем. Также и с именем кластера.

7) После чего просто запускаем экземпляр:

systemctl start postgresql@16-cluster2

Если что-то пошло не так, смотрим логи, читаем документацию и начинаем шаг за шагом траблшутить.

Конфиги будут все в той же директории /etc/postgresql/16/cluster2, так что придется их либо редактировать, потому что по сути дела у нас новый экземпляр, просто с другими данными.

Вы, наверное, уже поняли, почему в PostgreSQL назначают по одной БД каждому экземпляру. Работы чуть больше, зато управлять и бэкапить проще.

В 17 версии PostgreSQL обещают добавить инкремент в pg_basebackup. Если он будет работать так, как надо, то про сторонние средства бэкапа большинство админов смогут наконец-то забыть.

Самое главное: отработайте планы восстановления. Они должны у вас от зубов отскакивать. Создать резервную копию просто, а вот восстановить – уже нет (не потому, что это сложно, а потому, что это стресс и критическая ситуация).

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

https://man7.org/linux/man-pages/man1/tar.1.html

https://linux.die.net/man/1/gzip

https://manpage.me/?zstd

https://manpages.ubuntu.com/manpages/xenial/man1/lz4c.1.html

https://man7.org/linux/man-pages/man1/time.1.html

https://linux.die.net/man/1/rsync

https://man7.org/linux/man-pages/man1/cp.1.html

Отдельно про манифест и контрольные суммы

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

Manifest (манифест, доказательство, подтверждение – называйте как хотите) в плане резервного копирования – это файл, который содержит метаданные всей информации созданной резервной копии (схемы, объекты и т.п.).

Checksums (контрольные суммы) – это проверка манифеста. Контрольные суммы работают по алгоритмам SHA*, CRC32C или без него. По умолчанию CRC32C. Можете его оставить или поменять на какой хотите. Алгоритм используется для сравнения того, что написано в манифесте, с тем, что есть в самой резервной копии. Основная задача все же состоит в том, чтобы не допустить изменение информации непосредственно в резервной копии, а не в том, чтобы предотвратить повреждение данных в процессе создания бэкапа.

Учтите, что алгоритм потребляет ресурсы CPU. Поэтому попробуйте дважды создать бэкап продуктивной системы: с каким-либо алгоритмом и без него, просто для сравнения.

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

Самое главное: ни в коем случае не отключайте процесс подтверждения контрольных сумм и создание манифеста. За их отключение отвечают параметры --no-manifest и ‑‑no‑verify‑checksums.

Проверка

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

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

У различных дистрибутивов на различных версиях наблюдались проблемы с запуском этой утилиты, так что если напрямую ее вызвать не получилось, то просто запускайте из бинарников:

/usr/lib/postgresql/<major_version>/bin/pg_verifybackup

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

Аналогично и про утилиту pg_checksums, которая отдельно проверяет контрольные суммы.

Скорость проверки ограничена в первую очередь пропускной способностью дисковой подсистемы. Соответственно, чем больше объем кластера – тем дольше будет проверка. Нагрузка идет только на CPU. Не забивайте этим голову, разница будет видна только в больших системах. Но вы можете примерно определить, как долго будут сверяться контрольные суммы (по алгоритму CRC32C) на одном ядре CPU, например, так (для 1 GiB):

Смотрите только на real. Рекомендую все же запустить не менее 10 раз, выделить максимальное значение и прибавить к нему 10% (у меня 0m1.5131s). Так вы получите примерную скорость проверки контрольных сумм на 1 GiB данных (можете подставить свое значение, к примеру, 200 GiB). Учтите только, что GB и GiB это разные единицы измерения (а измеряем мы именно в GiB), пусть там и показан объем в GB, округленный до десятых. Напоминаю, что тестируем мы именно для одного ядра CPU. Система сама будет распараллеливать нагрузку между всеми ядрами. Однако я бы не советовал смотреть на итоговое время, которое вы получите путем деления времени обработки одним ядром на общее число ядер, потому что все системы разные, и нагрузки на них тоже. Но вы можете поделить на 3/4 от общего числа, это больше приближено к реальности.

Сам я никогда не сталкивался с битыми базами. Однако поискал примеры на форумах, и проблема везде одна: либо отказ дисковой подсистемы (и в целом проблемы с оборудованием), либо резервная копия была создана без потоковой репликации, и в бэкапе просто нет и не было нужных данных.

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

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

https://www.postgresql.org/docs/current/app-pgverifybackup.html

https://www.postgresql.org/docs/current/app-pgchecksums.html

Миграция

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

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

Миграция кластера на более новую версию возможна при помощи pg_upgradecluster (описана в самом начале) или pg_dumpall (можно вместе с pgdump).

Миграция кластера на старую мажорную версию возможна только при помощи pg_dumpall (+ pgdump). Причина миграции на старую версию всегда одна: шило в ж… то есть, желание админа как можно быстрее все обновить ради производительности и новых возможностей.

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

Восстановление кластера из дампа (логической копии)

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

Для восстановления всего кластера необходим дамп, созданный именно командой pg_dumpall.

Восстановление логической копии, созданной командой pg_dumpall, возможно только при помощи консоли psql, поскольку она выгружает данные в файл в формате plain, и это невозможно изменить (зато этот итоговый файл можно сжать).

С восстановлением копии отдельной базы через pg_dump все немного сложнее. Все зависит от того, в каком формате дамп был создан. Если он бы создан в формате plain, то восстановление возможно только через консоль psql. Если же он был создан в формате tar, directory или custom, то восстановление придется делать через утилиту pg_restore.

Форматы plain, custom, directory сжимаются алгоритмом zlib, который примерно в 10 раз медленнее, чем zstd. Поэтому работайте с форматом tar.

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

Если вы просто хотите переместить кластер или обновить его на более новую версию, то используйте pg_basebackup и pg_upgradecluster.

Пример миграции кластера через pg_dumpall

Тут в целом без разницы, мигрируем ли мы на новую версию или на старую. Порядок для них один и тот же. Учтите только, что процесс восстановления отличается от pg_basebackup, потому что здесь мы восстанавливаем ровно тот же кластер с теми же данными.

Миграция проходит в несколько шагов:

  • Определяем, какой кластер и куда будет смигрирован, а также его текущее расположение и порты подключения.

  • То же для целевого кластера.

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

  • Выгружаем дамп целого кластера при помощи команды pg_dumpall.

  • Восстанавливаем дамп через psql (главное, чтобы хватило места на диске).

Исходные данные:

Версия, имя и порт исходного кластера

16-cluster1:5433

Версия, имя и порт целевого кластера

15-cluster2:5434

Расположение исходного кластера

/var/lib/postgresql/16/cluster1

Расположение целевого кластера

/var/lib/postgresql/15/cluster2

Файл выгруженного дампа

/mnt/backup/pgdumpall.sql

Порядок действий

1) Запрещаем все подключения:

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

2) Выгружаем дамп кластера:

pg_dumpall -p 5433 -f /mnt/backup/pgdumpall.sql

Я указываю формат файла *.sql просто, чтобы было понятно, что это выгрузка SQL. Всегда так делаете. Особенно, когда работаете с архивами и сжатием: указывайте tar и название алгоритма, по которому файл архива был сжат. Иначе потом просто запутаетесь.

3) Восстанавливаем дамп на другом кластере (в консоль будут выведены все команды):

psql -p 5434 -f /mnt/backup/pgdumpall.sql

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

pg_dumpall также можно передать утилите сжатия zstd, например:

pg_dumpall -p 5433 | zstd > /mnt/backup/dumpall.sql.zst

Итог про резервное копирование

Вы, наверное, можете подумать: а вот у меня общий объем баз 200 GB, и мне что, по 5 раз в день их полностью перезаписывать?

Утилита pg_basebackup просто копирует весь кластер целиком, так что держать в кластере больше одной более-менее крупной базы нет смысла. Размазывайте нагрузку на хранилище для резервных копий равномерно, у вас же несколько кластеров. Не держите все базы в одном кластере, это вам не платный MS SQL Server. Используйте ее, если вам просто нужна резервная копия. А вот какая именно резервная копия, как она будет создаваться, как вы ее будете восстанавливать – это уже другие вопросы.

Вы можете еще связать команды pg_dumpall и pg_dump, чтобы копировать базы отдельно в многопоточном режиме, в интернете полно примеров. В 17 версии разработчики обещают добавить инкремент для физических резервных копий.

Разбор утилит pg_dump и pg_basebackup я сделал просто для того, чтобы вам в дальнейшем было значительно легче работать со сторонними системами резервного копирования (к примеру, с Barman, особенно если суммарный объем баз перевалил за 100 GB), потому что их документация рассчитана именно для тех, кто уже знает все основные вещи PostgreSQL, и разжевывать, к примеру, алгоритмы сжатия или репликацию там никто не будет.

Пятая часть:
https://habr.com/ru/articles/843324/

Комментарии (0)