Так исторически сложилось, что задача организации простого и понятного резервного копирования в мире PostgreSQL до сих пор не решена. Есть набор комьюнити-утилит, у каждой из которых есть некие плюсы, но всегда в нагрузку будет прорва минусов (тут нет инкрементных копий, там нет внятного расписания, это может только весь сервер вместо конкретной базы увозить и так далее). Да, есть тяжёловесный энтерпрайзный софт за много денег, зачастую требующий странного и работающий по какой-то своей логике, но это тоже не панацея. А вот чтобы просто и понятно, без головных болей организовать прозрачный процесс банального бекапа с инкрементами, работающим расписанием и восстановлением только того что надо — вот такого нет.
Но буквально на днях вышел PostgreSQL 17 и может там что-то изменилось? И да, и нет. Та самая манна небесная в виде pg_awesome_backup_tool так и не появилась, однако в релиз попал механизм walsummarizer, который обещает нативно отслеживать изменения в файлах баз данных, что позволит делать инкрементальные бекапы нативно и без лишних приседаний.
А чтобы не рассматривать новичка в вакууме, будем сравнивать его с ptrack — нашей (Postgres Professional) разработкой, которую наши любимые конкуренты уже расхватали в свои продукты и продают их как уникальнейшие решения.
Вводные данные
Не будем тратить время на пересказывание в тысячный раз всем известных истин о том, что такое инкрементальный бекап, чем он лучше дифференциального, и кто важнее в гонке RPO/RTO. Нам важен лишь факт необходимости иметь механизм для отслеживания изменений произошедших с нашей базой данных от момента создания последней точки бекапа до момента в котором нам захотелось создать инкрементальную к ней точку. Вариантов как это делать громадьё: от вычитывания WAL до отслеживания изменившихся секторов на дисках с вычитыванием из снапшота, поэтому переходим к нашим проектам на сегодня.
Дано
Из общих черт обоих инструментов: имеют открытый исходный код и отслеживание изменений в файлах данных основного слоя данных (main fork) и слое карты видимости (vm). А вот дальше начинаются отличия.
walsummarizer — проект Роберта Хааса, который был закомичен в мастер-ветку ванильного постгреса в 2023 году. Входит в состав PostgreSQL17 и не поддерживается младшими версиями.
Как сказано выше, умеет отслеживать только основной слой данных и vm. Запускает отдельный процесс в ОС, работая параллельно с postmaster, как отдельный компонент. Основной принцип работы: перечитывает WAL-файлы и пишет данные в отдельную папку pg_wal/summaries/. Подробнее разберём чуть ниже.
Соответственно, отмечает только изменённые страницы и ничего лишнего. В данный момент интегрирован исключительно с pg_basebackup.
ptrack — разработан Postgres Professional в 2017 году. В 2020 году на гитхаб выложена версия 2.0, где с тех пор регулярно обновляется. В отличие от walsummarizer поддерживает версии постгреса начиная с PostgreSQL 11, но поскольку не включен в ваниллу, требует патча в ядро. После чего надо будет собрать постгрес уже с ptrack.
Отслеживает не только основной слой данных и vm, но и изменения в карте свободного пространства (FSM). И, чисто технически, архитектура ptrack позволяет допилить его для отслеживания изменений в любых файлах постгреса, если будет такая необходимость. Подробности — чуть ниже.
Из других отличий стоит отметить, что ptrack не запускает отдельный процесс, а работает прямо в postmaster. И, что важно, ничего дополнительно не читает с дисков и не создаёт какой бы то ни было заметной нагрузки на сервер. Платить за это приходится определённым уровнем неточности, но в положительную сторону. То есть ptrack может отметить изменёнными больше данных, чем было на самом деле. Но, главное, что не меньше, т.е. консистентность не пострадает.
Ну и, само собой, ptrack поддержан во всей широте наших СУБД, от Standard до Enterprise редакций, включая все их фичи (CFS, Shardman и т.д.), начиная с v11.
Установка и настройка
В принципе, никаких сложностей тут нет (если вопрос пересборки постгреса не вызывает затруднений) и обе утилиты требуют указать несколько простейших параметров в обычном postgresql.conf
Для walsummarizer достаточно указать:
wal_level = ‘’replica # или ‘logical’
summarize_wal = on;
У ptrack немного по другому:
wal_level = ‘’replica # Можно использовать любой. Но replica или logical предпочтительнее
ptrack.map_size = 1000 # размер в МБ. Не будет меняться в течении работы
shared_preload_libraries = ‘ptrack’
После внесения изменений в случае walsumarizer достаточно перечитать файл конфигурации, в то время как для активации ptrack необходимо перезапустить сервер и сделать CREATE EXTENSION ptrack; для добавления его SQL-функций в постгрес.
Схема работы или как оно работает под капотом
Начнём с Walsummarizer. Работает он довольно бесхитростно.
Есть postmaster, который как-то пишет WAL. Совершенно отдельно от него, walsummarizer читает эти файлы, запускает функцию SummarizeWAL и пишет саммари-файлы в выбранную директорию. Как видим, очевидные минусы здесь это непредсказуемость запусков и работа в бесконечном цикле без пауз. То есть создаётся небольшая, но нагрузка на CPU и диск.
Теперь копнём чуть ниже, а именно посмотрим, как хранятся данные об изменённых страницах. Walsummarizer использует структуру roaring bitmap. Для того чтобы в неё что-то положить, он читает некий интервал LSN из WAL, присваивает ему tli (time line id) и создаёт файл с названием вида start_lsn/stop_lsn, чтобы сразу был понятен интервал. А чтобы итоговые саммари-файлы не хранились бесконечно, уничтожая ценное дисковое пространство, есть отдельный параметр wal_summary_keep_time. По умолчанию это десять дней.
Также есть набор SQL-функций, позволяющий получать информацию из саммари-файлов. Например, чтобы вынуть информацию в удобочитаемом виде, надо произвести две операции. Во-первых, получить список всех файлов в папке /summaries:
SELECT pg_available_wal_summaries();
pg_available_wal_summaries
----------------------------
(tli, start_lsn, end_lsn) #Это легенда. На всякий случай ;)
(1,0/18000028,0/18000130)
(1,0/19006F20,0/1902B228)
(1,0/19006DE8,0/19006F20)
(1,0/1B010900,0/1C000028)
(1,0/19000028,0/19006DE8)
(1,0/1A000028,0/1B010900)
(1,0/18000130,0/1800FCE8)
(1,0/1800FCE8,0/19000028)
(1,0/1902B228,0/1A000028)
(9 rows)
После чего определяемся, с какого LSN нам надо получить информацию об изменённых страницах и запрашиваем их, скопировав нужную строку.
SELECT
pg_wal_summary_contents('1','0/19000028','0/19006DE8');
pg_wal_summary_cntents
---------------------------
(relfilenode, reltablespace, reldatabase, relforknumber,
relblocknumber, is_limit_block) #ага, это тоже легенда
(1247,1663,16384,0,14,f)
(1249,1663,16384,0,16,f)
(1259,1663,16384,0,5,f)
(2608,1663,16384,0,3,f)
(1213,1664,0,0,0,f)
(16400,16399,16384,0,0,t)
(16400,16399,16384,2,0,t)
(16400,16399,16384,3,0,t)
(8 rows
В результате имеем увесистый список параметров. Например, довольно интересный is_limit_block, указывающий, что данные файлы были удален, если он выставлен в true. Или, что изменилась длинна записи, но это лучше в документации ознакомиться со всеми деталями. В любом случае, эта штука здорово ускоряет бекап.
Схема работы ptrack выглядит намного проще. При помощи патча ядра, о котором упоминалось в начале, в postmaster добавлено несколько хуков для отлова моментов записи на диск. На схеме отображены только три главных хука, но их больше. Суть в том, что когда postmaster решает записать что-либо на диск, например происходит чекпоинт, активируется определённый хук, который передаёт информация в карту изменённых файлов ptrack_map. Таким образом, в дальнейшем ptrack может отслеживать вообще любые изменения файлов на дисках, производимые postmaster.
Данные в карте хранятся с помощью фильтра Блума, в котором отмечаются LSN изменённых страниц, т.е. это не классический битовый хэш, а хэш-карта изменённых блоков с настраиваемым размером. Кстати, наша рекомендации по размеру это N/1024, где N — объём кластера в мегабайтах.
Кроме страниц, ptrack может отмечать в карте измененные файлы и так называемые батчи (страницы, сгруппированные по восемь). Если файл в карте не отмечен, значит все его страницы не менялись и их не надо проверять. То же самое верно для батчей, что здорово ускоряет процесс учета изменений. Однако фильтр Блума, как любая хэш-карта, допускает наличие коллизий, из-за чего у нас есть вероятность ложноположительного срабатывания. То есть случай, когда элемента в множестве нет, но структура данных утверждает, что есть. Таким образом мы можем случайно забрать в бекап лишний файл, но точно никогда ничего не пропустим.
И немного про SQL-запросы для работы с данными. Например, мы хотим узнать изменения, произошедшие с определённого LSN. Пишем запрос:
postgres=# SELECT * FROM ptrack_get_pagemapset('0/185C8C0');
path | pagecount | pagemap
----------------|-----------|-------------------------
base/16384/1255 | 3 | \x00100000050000000000
base/16384/2674 | 3 | \x00000000100000000000
base/16384/2691 | 1 | \x00000000000000000000
base/16384/2608 | 1 | \x00040000000000000000
base/16384/2690 | 1 | \x00040000000000000000
В ответе для каждого файла мы получаем количество изменённых страниц и битовую карту этих страниц внутри pagemap. Из-за необходимости делать итерацию по номерам страниц, в начале процесса бекапа ptrack pg_probackup немного задумывается, чтобы итерироваться по всем файлам в PGDATA и по их номерам страниц, но ничего криминального в плане задержек.
Немного практики с pg_basebackup и walsummarizer
Давайте же перейдём от теории к ежедневной рутине и попробуем сделать бекапы.
Для начала полный бекап:
$pg_basebackup -c fast -D backups/full --tablespacemapping=<old_tblspc_dir>=<new_tblspc_dir>
Ничего необычного, просто бекап в директорию backups/full в несжатом формате. Единственное, что стоит отменить, это использование параметра --tablespacemapping, чтобы бекап табличных пространств не остался в тех же папках, где они были созданы. Но если вы их не используете, то смело пропускайте.
Теперь переходим к интересному. С приходом walsummarizer инкрементные бекапы появились и в pg_basebackup, за что спасибо всё тому же Роберту Хаасу. Инкременты можно делать, как заведено, или от полного файла или от другого инкремента. То есть появилась возможность выстраивать полноценные цепочки бекапов.
Команда сделать инкремент от фула выглядит таким образом:
$ pg_basebackup -c fast -D backups/increment1 -i backups/full/backup_manifest --tablespacemapping=<old_tblspc_dir>=<new_tblspc_dir>
Цепочка инкрементов делается такой командой:
$ pg_basebackup -c fast -D backups/increment2 -i backups/increment1/backup_manifest --tablespacemapping=<old_tblspc_dir>=<new_tblspc_dir>
В обоих случаях главная часть это команда -i в аргументе, которой указывается путь на backup_manifest того бекапа, от которого мы хотим сделать инкремент. И ещё один нюанс про табличные пространства: для них надо указать новую папку, иначе basebackup будет ругаться. Скажет, что директория уже существует, она не пустая и всё удалит. Такая вот увлекательная архитектура была заложена, но как есть. Поэтому придётся выписываться заклинания а-ля new_tblspc_dir1, new_tblspc_dir2, new_tblspc_dir3. Грустно это, но пока вот так.
И самое интересное, это восстановление. С одной стороны, всё просто, с другой, требует подготовительных работ, чего во время аварии хотелось бы избежать. Так что удобство данного метода оставим за скобками, лишь только грустно вздохнём и набираем:
$ pg_combinebackup backups/full backups/increment1 backups/increment2 -o backups/full_combined
$ cp -r backups/full_combined/* pgdata_new/
$ cp -r <new_tblspc_dir>/* <old_tblspc_dir>
Рассмотрим, что тут произошло. Чисто технически, поскольку pg_basebackup сохраняет файлы из PGDATA в неизмененном виде, можно просто зайти в папку с бекапами и сделать там pg_ctl start и всё запустится. Но у нас же теперь есть инкременты (продолжаем делать вид, что в реальном мире никто не использует сжатие и предварительно надо всё распаковать), поэтому надо всё собрать в одну кучу с помощью новой утилиты pg_combinebackup от всё того же Хааса. На вход подаём ей директории бекапов, которые хотим объединить, и папку, куда сложить результат. Фактически, место для новой PGDATA, что мы и делаем парой команд cp после объединения. И да, если у нас есть табличные пространства, не забываем скопировать их тоже. А всё потому, что в постгресе так и не появилось встроенной утилиты для рестора, которой можно отдать все эти телодвижения на откуп.
Немного практики с ptrack и pg_probackup
Теперь посмотрим, как мы реализовали те же действия в своих решениях. Начинаем, как обычно, с полного бекапа.
$ pg_probackup init -B /backup_dir
$ pg_probackup add-instance -B /backup_dir -D /path/to/pgdata --instance 'main'
$ pg_probackup backup -B /backup_dir --instance 'main' -b FULL
Как видим, нам понадобилось выполнить две дополнительные команды. Первой мы создаём директорию для бекапов и рассказываем об этом pg_probackup. Второй создаём экземпляры резервных копий. Типичная вспомогательная история, которая проверит что везде есть нужные права, создаст конфиги и так далее.
Третьей командой мы уже создаём полный бекап указав директорию куда писать, экземпляр и явным образом указываем режим работы. В нашем случае FULL. Создание инкрементов выглядит так же, только FULL заменяется на ptrack
$ pg_probackup backup -B /backup_dir --instance 'main' -b PTRACK
pg_probackup версий 2.x умеет делать инкремент только от последнего бекапа в цепочке, поэтому ему не надо явно указывать родительский бекап. Но это именно во второй. Как выйдет третья, не надо так делать.
Если обратиться к документации по pg_probackup, там будет написано, что для создания инкрементов можно ещё использовать режимы PAGE и DELTA. Но по нашим замерам, в среднем, PTRACK получается несколько быстрее.
По сути, инкрементальный режим PAGE в pg_probackup повторяет функциональность walsummarizer. Но с той разницей, что PAGE читает WAL при запуске процесса бекапа, т.е. тратит намного больше времени. Вернее, скорее всего потратит намного больше времени, т.к. очевидно, что это напрямую зависит от количества произошедших между бекапами изменений. То есть, если наша база больше про чтение (мало записей WAL), то page будет быстрее, чем ptrack. Но таких баз намного меньше чем тех, где транзакции происходят активно, так что в неком усреднённом случае page будет медленнее.
Теперь рестор:
$ pg_probackup restore -B /backup_dir --instance 'main' -D /new_pgdata
Ничего неожиданного: указываем каталог с бекапами, экземпляр и папку для восстановления. Табличные пространства он сам поместит куда надо, за них можно не переживать. Главное, чтобы мажорные версии пробекапа и постгреса совпадали.
Выводы
Задачи сравнивать оба решения в лабораторных условиях не было, но даже по функциональному описанию можно сделать некоторые выводы.
Walsummarizer отличный проект с отличным набором функций. Как баги подправят, так будет вообще супер.
Ptrack явно эффективнее на маленьких высокопроизводительных базах (т.е. где WAL пишутся как сумасшедшие). На больших базах возрастает вероятность false-positive срабатывания в блум-фильтре.
Если мы делаем бекапы редко, ptrack будет эффективнее.
Если мы делаем бекапы часто, выигрывает walsummarizer.
Эффективность работы ptrack не зависит от частоты создания инкрементов или объёма накопленных изменений с последнего бекапа.
Размер ptrack.map_size прямо влияет на количество коллизий (больше карта — меньше коллизий). В данный момент её максимальный размер это 32 ГБ.
Ptrack тормозит на старте бекапа из-за необходимости вычитать свою карту и проитерироваться по номерам блоков файлов в PGDATA.
Walsummarizer даёт некий оверхед на процессор во время рутинной работы, но не во время бекапа.
Если читателя всё же интересно получить какие-то лабораторные данные с графиками и цифрами, то пишите, а мы попробуем реализовать.
Скрытый текст
Ну очевидно же, что очень хочется объединить плюсы обоих решений, откинув их минусы? Ждите pg_probackup v3 ;)
Комментарии (17)
Dhwtj
23.10.2024 10:18возврат БД postgres на произвольно выбранный момент времени путем накатывания реду логов на бекап начнет работать без боли когда - нибудь?
сейчас это делается ПРИМЕРНО так, извините за мой LLM
никогда так не делал ввиду лениЕсли у вас несколько баз данных на одном сервере PostgreSQL, и вам нужно восстановить только одну из них, процесс будет немного отличаться. Вот пошаговая инструкция для такого случая:
-
Создайте новый кластер PostgreSQL для восстановления:
pg_createcluster [version] recovery
Например:
pg_createcluster 16 recovery
-
Остановите новый кластер:
pg_ctlcluster [version] recovery stop
-
Очистите каталог данных нового кластера:
sudo rm -rf /var/lib/postgresql/[version]/recovery/*
-
Восстановите базовый бэкап только нужной базы данных в новый кластер:
pg_restore -C -d postgres -U postgres -h localhost -p [port_of_new_cluster] [path_to_backup_file]
Флаг
-C
создаст базу данных, если она не существует. -
Создайте файл recovery.signal в каталоге данных нового кластера:
sudo touch /var/lib/postgresql/[version]/recovery/recovery.signal
-
Отредактируйте postgresql.conf нового кластера:
sudo nano /var/lib/postgresql/[version]/recovery/postgresql.conf
Добавьте следующие строки:
restore_command = 'cp /path/to/archive/%f %p' recovery_target_time = '2024-10-24 14:30:00' recovery_target_action = 'promote'
-
Запустите новый кластер:
pg_ctlcluster [version] recovery start
-
Мониторьте процесс восстановления в логах:
sudo tail -f /var/log/postgresql/postgresql-[version]-recovery.log
-
После завершения восстановления, проверьте состояние базы данных:
psql -p [port_of_new_cluster] -d [restored_db_name]
-
Если все в порядке, вы можете остановить оригинальный кластер и скопировать восстановленную базу данных обратно:
pg_dumpall -p [port_of_new_cluster] -d [restored_db_name] | psql -p [port_of_original_cluster] -d postgres
-
Удалите временный кластер:
pg_dropcluster [version] recovery
Этот метод позволяет восстановить одну базу данных, не затрагивая остальные базы на сервере. Он создает временный кластер для восстановления, что минимизирует риск повреждения данных в основном кластере.
Важные замечания:
Убедитесь, что у вас достаточно места на диске для создания временного кластера.
Проверьте, что порты нового и оригинального кластеров не конфликтуют.
Всегда делайте резервную копию перед выполнением операций восстановления.
В зависимости от версии PostgreSQL, некоторые команды или файлы конфигурации могут слегка отличаться.
Loxmatiymamont Автор
23.10.2024 10:18Люди ответственные за разработку баз данных, обычно, хорошо понимают в базах данных, но не в том как их обслуживать на местах. С этого попался и началась: нативных внятных тулов для такой простой операции как бекап - нет.
-
Sleuthhound
Спасибо за статью.
Подскажите пожалуйста, у pg_probackup v3 будет все таки бесплатная версия или он будет доступен только как часть Enterprise-редакции?
Loxmatiymamont Автор
Это станет известно ближе к релизу, потому что вот так звёзды складываются, что прямо сейчас никто вам на этот вопрос точно не ответит.
Testman2023
Если читать доки, что сейчас доступно по версиям:
Можно собрать pg_probackup
https://github.com/postgrespro/pg_probackup
или поставить из реп:
https://postgrespro.github.io/pg_probackup/
pnetmon
но поддержка 17 не заявлена на https://github.com/postgrespro/pg_probackup
как и тут https://repo.postgrespro.ru/pg_probackup/deb/pool/main-noble/p/ нету 17
а для своих продуктов они используют не публичную версию которая в нумерации давно ушла вперед
Sleuthhound
К сожалению да, я тоже давно заметил, что публичная версия pg_probackup v2 безнадежно отстала от закрытой версии. Только мелкие багфиксы и никакого развития :(
Loxmatiymamont Автор
А чего конкретно не хватает в публичной, из того что есть в закрытой?
Sleuthhound
Встречный вопрос - а что есть в закрытой? Она на то и закрытая, что ченджлог доступен только энтерпрайз пользователям.
В открытой нехватает хранения бэкапов в s3
Loxmatiymamont Автор
Но вы так уверенно говорите, что публичная версия пробекапа безнадёжно отстала и там никакого развития, будто у вас есть список того чем обделили.
Sleuthhound
Список то есть. Посмотрите на количество issue, их 160 штук и кажется что они никогда не будут закрыты. Там и ошибки и предложения нового функционала, например 2 моих предложения раз и два - и вроде бы не сложно и вроде как нужно, но не реализовано и в то же время не отклонены. Или реализовано, но в закрытой версии? Тут я не знаю тк доступа к закрытой версии у меня нет.
Loxmatiymamont Автор
Супер, спасибо. Насколько я знаю PM'ы ишуи регулярно смотрят и самое интересное берётся в разработку. В ent версии действительно что-то уже сделано, но, как я говорил выше, надо дождаться выхода тройки, потому что основные усилия разработчиков сейчас там.
hogstaberg
Ну, если честно, то первое предложение - прямо классический кейс когда проще дописать и PR прислать, чем просить и ждать.
Sleuthhound
Ну если честно, то сомнительно, что туда что-то примут. Посмотрите список PR, висящих годами.
hogstaberg
Попытка - не пытка
Testman2023
Я в курсе, что есть разница в версиях pg-probackup:
Инструкция по установке Postgres Pro Standard 16.4.1 не работает.
https://postgrespro.com/products/download
Postgres Pro Std 17 в репах пока нет.