
Greengage DB — это массивно-параллельная реляционная СУБД на базе Greenplum OSS, которая подходит для хранения и обработки данных. Позволяет выполнять сложные аналитические запросы над большими объёмами данных, предоставляя к ним гетерогенный доступ за счёт различного рода коннекторов и средств интеграции.
Но помимо функциональных возможностей, есть и ряд других необходимых вещей, таких как мониторинг, аудит, резервирование и пр. Они требуются для обеспечения полноценной и надёжной работы системы, особенно если речь идет о промышленной эксплуатации. В рамках данной статьи как раз хочется обсудить подход к резервированию кластера Greengage: какие тут есть возможности, каковы подводные камни и многое другое.
Вначале осветим базовые знания, необходимые для понимания процесса резервного копирования, а затем уже перейдём к практическому подходу по резервированию Greengage. Тем, кого теория не интересует, можно сразу перейти к разделу Резервирование Greengage.
Как может выглядеть резервирование кластера?
Начнём с того, что резервирование кластера — это необязательно про создание резервных копий (РК). В первую очередь резервирование — это про отказоустойчивость и высокую доступность, чего в случае кластерных систем хранения можно достигать за счёт:
репликации данных (наличие синхронной или асинхронной реплики);
auto-failover (PostgreSQL Patroni + etcd/ZooKeeper);
multi-master (PostgreSQL BDR от EDB);
резервное копирование (pg_dump, pg_basebackup, gpbackup);
инкрементальные бэкапы (архивирование WAL);
geo-replication (логическая репликация между DC через CDC);
использование оркестраторов (k8s + StatefulSets).
По большей части всё зависит от возможностей системы, бюджета, а также выбора между согласованностью данных и доступностью сервиса (по CAP-теореме). Кроме этого, в контексте кластерной СУБД различные варианты резервирования можно группировать по «температуре» данных:
-
Горячее резервирование (Hot Standby / Active-Active):
Данные доступны мгновенно для чтения/записи.
Полная синхронная или почти синхронная репликация.
Высокая стоимость решения.
-
Тёплое резервирование (Warm Standby / Active-Passive):
Реплики отстают от мастера на несколько секунд/минут.
Данные доступны для чтения, но запись возможна только на мастер.
Может быть асинхронная или полусинхронная (write majority) репликация.
-
Холодное резервирование (Cold Backup / Delayed Replica):
Данные обновляются редко.
Хранятся в бэкапах или архивах.
Восстановление требует затрат по времени.
В Greengage (как и в Greenplum) резервирование поддерживается следующими средствами:
зеркалирование сегментов (наличие синхронной реплики для каждого сегмента кластера);
логическое резервирование (gpbackup/gprestore);
Далее будет предложено рассмотреть ещё один вариант резервирования посредством снятия физической РК. Здесь будет представлено работающее решение, но с которым не стоит идти в production. Цель — поделиться представлением о том, какой подход к формированию физической РК может быть применён, на какие нюансы стоит обратить внимание и какие у такого подхода есть недостатки.
А почему бинарный?
Бинарные резервные копии часто используются для создания реплик, восстановления после сбоя, резервного копирования баз большого объёма. Но это больше про ванильный PostgreSQL. Если говорить про кластер Greengage, то за счёт резервирования отдельных сегментов можно распараллелить как снятие резервных копий, так и восстановление из них. Кроме этого, можно восстановить зеркало каждого сегмента из той же РК сегмента.
Но для начала давайте разберёмся, как должна выглядеть бинарная резервная копия и как её получить.
Физическое резервирование
Поскольку Greengage базируется на ядре PostgreSQL, то для начала надо разобраться: а что такое физическое резервирование в PostgreSQL?
В самом простом варианте бинарной РК можно считать ручное копирование PGDATA
-директории. Для начала нужно настроить архивирование WAL, а далее, в дополнение к резервной копии, хранить архивы WAL. Чтобы начать процесс резервирования, следует выполнить запрос:
SELECT pg_start_backup('/mnt/backup/2025/04/', true);
Далее необходимо скопировать файлы из PGDATA
, после чего следует остановить процесс резервирования, выполнив:
SELECT pg_stop_backup();
Но стоит ли копировать все данные? На самом деле, нет. Чтобы не запутать PostgreSQL в процессе восстановления, из РК следует исключить следующие данные:
backup_label.old
(создаётся при прерывании резервирования);postmaster.pid
иpostmaster.opts
(будут влиять на работуpg_ctl
);recovery.conf
/postgresql.auto.conf
(должен быть создан после восстановления из РК для запуска процесса recovery);recovery.done
(означает завершение процесса recovery);каталоги
pg_replslot/
,pg_dynshmem/
,pg_notify/
,pg_serial/
,pg_snapshots/
,pg_stat_tmp/
иpg_subtrans/
(содержимое инициализируется при запуске postmaster).
Кроме вышеперечисленных, есть ряд файлов, которые содержат специфичные для Greengage настройки, и их стоит учитывать при восстановлении. К таким относятся:
gpperfmon/conf/gpperfmon.conf
(файл конфигурации расширенияgpperfmon
, хранится только на мастере);internal.auto.conf
(соответствуетdbid
инстанса кластера в таблицеgp_segment_configuration
);postgresql.conf
(содержит специфичную настройкуgp_contentid
с идентификатором сегмента кластера).
Как видно, уже в самом процессе резервирования есть множество нюансов, которые приходится учитывать, пользуясь низкоуровневым API для снятия физического бэкапа. Вместо выполнения данных действий вручную, есть поставляемая вместе с PostgreSQL утилита pg_basebackup, автоматизирующая часть этапов физического резервирования. Также имеется ряд утилит, которые расширяют базовые возможности физического резервирования (pgbackrest
, wal-g
, barman
) и упрощают управление резервированием. Далее в примерах мы будем строить решение, используя pgbackrest
, но вы можете использовать любой удобный для вас вариант.
Point-in-time recovery
Восстановление на момент времени (Point-in-time recovery или PITR) — это процесс восстановления из архивов WAL на заданную точку в WAL. В зависимости от версии PostgreSQL, в качестве точки восстановления (recovery_target
) можно задавать LSN
(идентификатор записи WAL), xid
(идентификатор транзакции), time
, restore point
(имя точки восстановления). Подробнее можно прочитать в официальной документации.
PITR напрямую зависит от физической резервной копии: сначала происходит восстановление из последней, а далее в конфигурации восстановления кластера PostgreSQL можно задать точку, до которой требуется произвести восстановление. Это позволяет восстанавливать данные, которые были созданы/изменены уже после завершения снятия резервной копии. Нам это понадобится в дальнейшем для формирования консистентной резервной копии кластера Greengage.
Непрерывное архивирование
Непрерывное архивирование — ключевой механизм для отказоустойчивости и минимизации потерь данных. В сочетании с полной РК (pg_basebackup
) это даёт возможность восстановить базу до любого доступного состояния.
Состав резервной копии
До этого момента мы говорили только про резервирование кластера PostgreSQL. Теперь настало время подумать про копию кластера. Если одновременно запустить снятие физической копии с каждого сегмента кластера и дождаться завершения резервирования у всех сегментов, то можно ли совокупность этих копий считать резервной копией кластера? И ответ — нет. При таком подходе не гарантируется согласованность данных на уровне кластера, поскольку завершение копирования у каждого сегмента будет в свой момент времени.
Чтобы решить эту проблему, достаточно после завершения резервирования всех сегментов создать распределённую именованную точку при помощи функции gp_create_restore_point(<rp_name>)
, которая доступна из расширения gp_pitr
и в качестве аргумента принимает имя точки восстановления.
Таким образом, бэкап кластера будет выглядеть так:
список физических резервных копий для каждого сегмента;
имя restore point, созданного после завершения резервирования сегментов;
архивы WAL каждого сегмента с момента начала резервирования до архива, куда был записан restore point.
Теперь физических данных должно хватать для восстановления кластера. Но как быть с метаданными самого кластера, ведь если он будет недоступен, то откуда взять информацию по его хостам и распределению сегментов? Решений тут может быть много. Как вариант, предлагается дополнительно, сразу перед запуском резервирования, получить состояние таблицы gp_segment_configuration
и сохранить его в дополнение к метаданным резервной копии. Это позволит не только упростить процесс восстановления, но и при необходимости произвести дополнительные проверки.
Резервирование Greengage
Для работы с физическими копиями используется утилита pgbackrest
, потому что она предоставляет гораздо больше возможностей по работе с резервными копиями относительно pg_basebackup
.
Кто-то может задаться вопросом: почему не использовать, к примеру, WAL-G, ведь он уже поддерживает резервирование для кластера Greenplum, что по сути должно работать и для Greengage? И правда, его можно использовать в ряде случаев как инструмент резервирования. Но цель данной статьи больше обучающая, а именно: дать представление о подходе к резервированию кластерной СУБД, чтобы понимать какие тут могут быть нюансы и ограничения.
Подготовка окружения
Итак, следуя предыдущим рассуждениям, давайте сформируем резервную копию кластера Greengage. Но для начала надо подготовить необходимое окружение:
1. Собираем pgbackrest с поддержкой Greengage.
meson setup build --prefix=$GPHOME --libdir=$GPHOME/lib --pkg-config-path=$GPHOME/lib/pkgconfig
2. Устанавливаем pgbackrest
на каждый хост кластера и даём права на запуск для пользователя gpadmin
.
3. Настраиваем pgbackrest
для работы с сегментами каждого хоста кластера. Для этого создаём файл конфигурации pgbackrest.conf
на каждом хосте кластера.
Пример pgbackrest.conf
[global]
repo1-type=s3
repo1-path=/
repo1-s3-bucket=prod
repo1-s3-endpoint=https://s3storage:9400/
repo1-s3-region=region
repo1-s3-key-type=shared
repo1-s3-key=IbeqK139x8jZVlfFXVNZ
repo1-s3-key-secret=PEU8HQpagkpKojgzplHTgBWAqXBYl0CqZw1vBtDn
repo1-s3-uri-style=path
repo1-storage-upload-chunk-size=5242880
repo1-storage-verify-tls=n
repo1-retention-full=3
repo1-retention-archive-type=full
repo1-retention-archive=3
repo1-retention-diff=6
repo1-bundle=y
repo1-bundle-limit=2MB
repo1-bundle-size=100MB
repo1-block=n
lock-path=/tmp/pgbackrest
exclude=pg_log/gp_era
exclude=backups
exclude=gpperfmon/data/
exclude=gpbackup_history.yaml
archive-timeout=60.0
db-timeout=1800.0
protocol-timeout=1830.0
io-timeout=60.0
compress-type=gz
compress-level=6
buffer-size=1MB
process-max=20
log-level-console=info
log-level-file=info
log-path=/home/gpadmin/gpAdminLogs
start-fast=y
expire-auto=n
fork=GPDB
checksum-page=n
[seg0]
pg1-path=/data1/primary/gpseg0
pg1-port=10000
[seg1]
pg1-path=/data1/mirror/gpseg1
pg1-port=10501
В секции global
идут общие настройки для всех сегментов. Среди прочих, стоит обратить внимание на следующие:
настройки репозитория для хранения архивов WAL и резервных копий (параметры
repo1-*
);список исключаемых из резервной копии файлов и каталогов;
параметр
fork
, который при выставлении значенияGPDB
умеет правильно работать с сегментами Greengage;отключение автоматического удаления резервных копий (параметр
expire-auto
).
Далее идут секции (в терминологии pgbackrest
это stanza
), специфичные для каждого сегмента конкретного хоста. Имя секции имеет формат seg<content>
(<content>
— это идентификатор сегмента из gp_segment_configuration.content
). В самой секции указывается информация из gp_segment_configuration
. Важно, что в данном файле указываются секции как для primary-, так и для mirror-сегментов хоста. За счёт этого в случае переключения с primary на mirror процесс архивирования WAL не будет нарушен.
4. На каждом хосте кластера настраиваем переменную окружения PGBACKREST_CONFIG
, где указываем абсолютный путь до файла конфигурации pgbackrest.conf
.
5. На кластере Greengage включаем режим архивирования WAL:
gpconfig -c archive_mode -v on
gpconfig -c archive_command -v ''\"'\"'PGOPTIONS=\"-c gp_session_role=utility\" pgbackrest --stanza=seg%c archive-push %p'\"'\"'' --skipvalidation
При выполнении архивации %c
будет заменено на идентификатор сегмента, а %p
— на путь до файла для архивирования. Применение изменений требует перезагрузки кластера:
gpstop -arM fast
6. Устанавливаем расширение gp_pitr
:
CREATE EXTENSION gp_pitr;
Создание резервной копии
Для целей демонстрации подготовлена небольшая утилита ggbm, которая реализует базовый вариант резервирования и восстановления кластера Greengage.
Далее сам процесс резервирования:
1. Берём блокировку на выполнение операции на кластере. Это может быть lock-файл, синхронизация через внешний координатор или что-то еще. Важно то, что в один момент времени над кластером может выполняться только одна операция (резервирование, восстановление, создание restore point, удаление резервной копии и т.д.).
2. Берём текущую топологию и сохраняем её в отдельный файл:
SELECT dbid, content, role, preferred_role, port, hostname, address, datadir
FROM gp_segment configuration;
Стоит добавить, что нас не интересуют значения mode
и status
, потому что при восстановлении они зависят от того, будут ли восстанавливаться зеркала или нет (и каким способом).
3. Запускаем резервирование на мастере и каждом основном сегменте кластера. Тип резервной копии должен быть одинаковым для всех сегментов, а имя stanza
соответствовать значению content
сегмента:
gpssh -h sdw1 -v -e 'PGOPTIONS="-c gp_session_role=utility" pgbackrest --type=full --stanza=seg0 backup'
Дожидаемся кода ответа 0
от каждого процесса pgbackrest
. Ниже пример вывода при завершении резервирования:
[mdw1] 2025-04-30 21:18:39.086 P00 INFO: backup start archive = 000000010000000000000011, lsn = 0/44000028
[mdw1] 2025-04-30 21:18:39.086 P00 INFO: check archive for prior segment 000000010000000000000010
[mdw1] 2025-04-30 21:18:54.523 P00 INFO: execute exclusive backup stop and wait for all WAL segments to archive
[mdw1] 2025-04-30 21:18:56.525 P00 INFO: backup stop archive = 000000010000000000000011, lsn = 0/440000D0
[mdw1] 2025-04-30 21:18:56.525 P00 INFO: check archive for segment(s) 000000010000000000000011:000000010000000000000011
[mdw1] 2025-04-30 21:18:56.543 P00 INFO: new backup label = 20250430-211837F
[mdw1] 2025-04-30 21:18:56.656 P00 INFO: full backup size = 289MB, file total = 1714
[mdw1] 2025-04-30 21:18:56.656 P00 INFO: backup command end: completed successfully (19582ms)
[INFO] completed successfully
Также для каждого сегмента сохраняем в метаданные резервной копии имя сформированного backup set (можно получить из лога или вывода pgbackrest info
для конкретной stanza
).
4. Создаём restore point и выполняем принудительное переключение WAL лога на следующий сегмент:
adb=# SELECT gp_segment_id, restore_lsn, pg_xlogfile_name(restore_lsn) as walfile FROM gp_create_restore_point('rp_1');
gp_segment_id | restore_lsn | walfile
---------------+-------------+--------------------------
-1 | 0/1CA5DD80 | 000000010000000000000007
0 | 0/37082A00 | 00000001000000000000000D
1 | 0/370E4480 | 00000001000000000000000D
(3 rows)
adb=# SELECT pg_switch_xlog() UNION ALL SELECT pg_switch_xlog() FROM gp_dist_random('gp_id');
(Пример для GG6. В GG7 функция pg_xlogfile_name
переименована в pg_walfile_name
, а pg_switch_xlog
— в pg_switch_wal
).
5. Сохраняем все метаданные.
Пример backup
В случае с ggbm процесс резервирования будет выглядеть следующим образом:
:~$ ggbm.py backup -n full_1 -t full
Backup completed successfully. Metadata saved to backup_full_1.json
Operation completed successfully
В результате будет сформирована резервная копия кластера с именованной точкой full_1
, а метаданные сохранены в файле backup_full_1.json
:
{
"backup_name": "full_1",
"restore_point": "full_1",
"created_at": "2025-05-07T10:42:33.741219",
"segments": [
{
"dbid": "1",
"content": "-1",
"role": "p",
"preferred_role": "p",
"mode": "n",
"status": "u",
"port": "5432",
"hostname": "vdv-h1",
"address": "vdv-h1",
"datadir": "/data1/master/gpseg-1",
"backup_success": true,
"backup_label": "20250507-104217F"
},
{
"dbid": "2",
"content": "0",
"role": "p",
"preferred_role": "p",
"mode": "n",
"status": "u",
"port": "10000",
"hostname": "vdv-h2",
"address": "vdv-h2",
"datadir": "/data1/primary/gpseg0",
"backup_success": true,
"backup_label": "20250507-104217F"
},
{
"dbid": "3",
"content": "1",
"role": "p",
"preferred_role": "p",
"mode": "n",
"status": "u",
"port": "10000",
"hostname": "vdv-h3",
"address": "vdv-h3",
"datadir": "/data1/primary/gpseg1",
"backup_success": true,
"backup_label": "20250507-104217F"
}
]
}
По итогу мы получили полную резервную копию с точкой синхронизации. Далее можно создавать новые резервные копии (в том числе дифференциальные и инкрементальные) или только именованные точки (восстановление на которые следует делать из ближайшей резервной копии).
Восстановление кластера
Теперь перейдём к процессу восстановления кластера. Очевидно, что перед восстановлением у нас уже должна иметься хотя бы одна резервная копия кластера. Но нельзя сразу приступать к восстановлению, не проверив ряд важных критериев:
Число primary-сегментов на кластере должно совпадать с числом primary-сегментов в резервной копии.Иными словами, если на кластере был выполнен expand/shrink после снятия резервной копии, то восстановиться на топологию до остановки кластера не получится (но можно восстановить кластер на топологию на момент снятия резервной копии). Проверку можно сделать, только если до начала восстановления кластер доступен и можно обратиться к таблице
gp_segment_configuration
.Кластер должен быть остановлен. Восстановление из физической копии возможно только на остановленном кластере.
Все процессы сегментов должны быть остановлены на всех хостах. Иначе могут быть проблемы при старте или выполнении recovery.
На всех хостах кластера должна быть конфигурация
pgbackrest.conf
, которая настроена на репозиторий с нужной резервной копией.
Если все вышеуказанные условия выполнены, то можно приступать непосредственно к восстановлению кластера. Основными шагами будут:
(Опционально) Удалить всё содержимое
PGDATA
на мастере и primary-сегментах. Если планируете выполнить полную перезапись директории, этого можно не делать, добавив аргумент--force
на следующем шаге.-
Запустить процесс восстановления из резервной копии на мастере и каждом primary-сегменте (основываясь на топологии кластера):
gpssh -h sdw1 -v -e 'PGOPTIONS="-c gp_session_role=utility" pgbackrest --set=20250430-211837F --stanza=seg0 --delta --recovery-option="recovery_target_action=shutdown" restore'
По окончании автоматически будет создана конфигурация для выполнения процесса recovery для каждого сегмента.
-
Дождаться успешного завершения предыдущего шага, а затем запустить кластер:
gpstart -a
-
Восстановить зеркала:
gprecoverseg -aF
Восстановить standby через его пересоздание.
Пример restore
Для восстановления кластера при помощи утилиты ggbm выполним следующую команду:
:~$ ggbm.py restore -n full_1
Cluster successfully restored from backup full_1
Operation completed successfully
Таким образом, будут восстановлены master и primary-сегменты, которые были в роли primary на момент снятия РК. Для завершения процесса восстановления, а также поднятия зеркал и standby нужно проделать шаги 3-5 из раздела Восстановление кластера.
Итоги
Итак, мы рассмотрели подход к резервированию данных распределённой системы на примере Greengage DB. При довольно большой сложности резервирования кластера данным способом, всё же у него есть и ряд преимуществ, такие как:
-
Хорошая масштабируемость, поскольку скорость резервирования можно масштабировать за счёт:
большего числа сегмент-хостов;
вертикального масштабирования сегмент-хостов и увеличения числа потоков при выполнении резервирования/восстановления;
использования нескольких репозиториев для шардирования хранения резервных копий сегментов кластера.
Гарантированная консистентность данных, которая достигается средствами СУБД.
Переиспользуемость физических копий. За счёт них можно создать полную копию оригинального кластера или производить частичное восстановление баз данных.
Однако стоит признать, что в данной статье не были покрыты некоторые вопросы, которые являются важными для полноценного использования физического резервирования кластера в промышленной среде. Основным является то, что приведённый подход реализует in-place восстановление, то есть восстановление возможно только на том же окружении, с которого была снята резервная копия. Также не были покрыты следующие вопросы:
Как обрабатывать исключительные ситуации при резервировании/восстановлении?
Как восстановить зеркало одновременно с основным сегментом?
Как управлять удалением резервных копий?
Как проверять корректность резервных копий?
Насколько физическое резервирование эффективнее логического?
Как восстанавливаться на других хостах?
В рамках следующих статей мы как раз обратимся к решению такого рода вопросов и посмотрим на преимущества, которые достигаются за счёт использования физического резервирования.