Введение в PostgreSQL BDR
![image](https://habrastorage.org/getpro/habr/post_images/025/cd1/ddc/025cd1ddcfe0fdd580cd717dd09a708a.jpg)
PostgreSQL это не только стабильная и надежная СУБД но и плюс ко всем это динамично развивающийся продукт, в котором от релиза к релизу появляются самые разные прорывные вещи. В свое время одной из таких технологий была потоковая репликация. Это высокопроизводительная репликация которая позволяет очень легко и дешево масштабировать базу данных на чтение. Используя ее можно создавать надежные конфигурации распределяя нагрузку на чтение между узлами. Однако как я написал выше, продукт развивается, и сегодня в статье речь пойдет о новой технологии BDR (Bi-Directional Replication).
Немного терминов для тех кто не в теме:
WAL (Write Ahead Log) — журнал транзакции, на нем основана встроенная потоковая репликация в постгресе, СУБД пишет туда все что происходит с данными в БД.
SR (Streaming Replication) — общее название встроенной потоковой репликации которая которая основана на WAL, все что пишется в WAL, затем отправляется на слейвы и воспроизводится. Бывает физическая и логическая потоковая репликация.
PLSR (Physical Log Streaming Replication) — физическая потоковая репликация (то что уже реализовано и работает), все что попало в WAL без последующего разбора реплицируется на слейв сервера, это и изменение данных/схемы и более низкоуровневые вещи (full page writes, vacuum, hint bit settings).
LLSR (Logical Log Streaming Replication) — логическая потоковая репликация (появится в 9.4) также основана на WAL журналах, но уже более интеллектуальная и для репликации извлекается только определенная часть журналов в которых описываются изменения схемы и данных БД (то есть некоторые низкоуровневые вещи отсеиваются).
Что скрывается под термином BDR?
BDR (Bi-Directional Replication) это новая функциональность добавленая в ядро PostgreSQL которая предоставляет расширенные средства для репликации. На данный момент это реализовано в виде небольшого патча и модуля. Заявлено что полностью будет только в PostgreSQL 9.5 (сейчас 9.3-stable и 9.4-beta1).
Если коротко, то BDR позволяет создавать географически распределенные асинхронные мульти-мастер конфигурации (о да, детка) используя для этого встроенную логическую потоковую репликацию LLSR.
Тем не менее, BDR не является инструментом для кластеризации, т.к. здесь нет каких-либо глобальных менеджеров блокировок или координаторов транзакций (привет Postgres-XC/XL). Каждый узел не зависит от других, что было бы невозможно в случае использования менеджеров блокировки. Каждый из узлов содержит локальную копию данных идентичную данным на других узлах. Запросы также выполняются только локально (чтобы было более понятно о чем речь, приведу сравнение с Postgres-XC/Postgres-XL, там все серверы работают как бы в одной упряжке, транзакции рулятся глобальным менеджером транзакции, а запросы от приложения поступают на координатора(ов) который отправляет выполняться пришедшие запросы на любой рабочий узел, вот). При этом каждый из узлов внутренне консистентен в любое время, целиком же группа серверов является согласованной в конечном счете (eventually consistent).
Уникальность BDR заключается в том что она непохожа ни на встроенную потоковую репликацию, ни на существующие trigger-based решения (Londiste, Slony, Bucardo).
Самым заметным отличием от потоковой репликации является то, что BDR (LLSR) оперирует базами (per-database replication), а классическая PLSR реплицирует целиком инстанс (per-cluster replication), т.е. все базы внутри инстанса.
Существующие ограничения и особенности:
1. Все изменения данных вызываемые INSERT/DELETE/UPDATE реплицируются (TRUNCATE на момент написания статьи пока не реализован)
2. Большинство операции изменения схемы (DDL) реплицируются успешно. Неподдерживаемые DDL фиксируются модулем репликации и отклоняются с выдачей ошибкой (на момент написания статьи не работал CREATE TABLE… AS)
3. Определения таблиц, типов, расширений и т.п. должны быть идентичными между upstream и downstream мастерами.
4. Действия которые отражаются в WAL, но непредставляются в виде логических изменений не реплицируются на другой узел (запись полных страниц, вакуумация таблиц и т.п.). Таким образом логическая потоковая репликация (LLSR) избавлена от некоторой части накладных расходов которые присутствуют в физической потоковой репликации PLSR (тем не менее это не означает что LLSR требуется меньшая пропускная способность сети чем для PLSR).
Итак пожалуй хватит теории, немного практики. Уже сейчас есть возможность потестировать Bi-Directional репликацию.
Установка выполняется на двух виртуальных машинах с CentOS 6.5 minimal. Устанавливаем необходимые для сборки пакеты:
# yum install readline-devel zlib-devel yum-utils -y
# yum groupinstall "Development Tools" -y
Переходим в аккаунт postgres и выполняем установку postgresql с поддержкой BDR. Тут стоит отметить что парни из 2ndQuadrant написали установщик чтобы желающие попробовать не прилагали больших усилий по установке и настройке, за что им пучок зелени.
# su - postgres
$ curl -s "http://git.postgresql.org/gitweb/?p=2ndquadrant_bdr.git;a=blob_plain;f=contrib/bdr/scripts/bdr_quickstart.sh;hb=refs/heads/bdr-next" | bash
Добавляем каталог с исполняемыми файлами postgres в переменную окружения PATH и тут же проверяем на psql. Кто не знает, команда export одноразовая, поэтому если вы планируете долго использовать или играться с BDR, то добавьте эту команду в .bashrc вашего пользователя (если у вас bash конечно же).
$ export PATH=$HOME/2ndquadrant_bdr/bdr/bin:$PATH
$ psql --version
psql (PostgreSQL) 9.4beta1_bdr0601
Инициализируем каталоги баз данных на обоих узлах после чего сразу же запускаем. Предварительно править postgresql.conf необязательно, при первом запуске мы создадим тестовую базу которая и будет в дальнейшем реплицироваться.
$ initdb -D data/ -A trust -U postgres
$ pg_ctl -l logfile -D data/ -w start
$ psql -c 'create database staging_db'
Создали базу, после чего переходим к настройке postgresql.conf. Сначала настраиваем upstream мастер. В конфигурации ниже, мы указываем необходимость загрузки библиотеки bdr (shared_preload_libraries), определяем уровень подробности WAL журналов в значение logical (wal_level), определяем количество слотов для репликации, максимально возможное количество процессов занятых в отправке WAL журналов (wal_senders) и включаем трекинг времени для операции COMMIT что необходимо для разрешения конфликтов (last-UPDATE-wins). Затем в конце файла определяем конфигурацию для BDR: указываем название соединения и настройки для подключения к удаленному узлу. Стоит отметить, что имя указываемое в bdr.connections является произвольным (у меня это имя виртуальной машины), главное что указанное имя должно участвовать в именах нижележащих параметров.
$ vi data/postgresql.conf
listen_address = '*'
shared_preload_libraries = 'bdr'
wal_level = logical
wal_senders = 4
max_replication_slots = 4
track_commit_timestamp = on
bdr.connections = 'vm13'
bdr.vm13_dsn = 'host=192.168.122.13 port=5432 user=postgres dbname=staging_db'
Теперь конфигурация downstream мастера. Сначала привожу описание конфигурации и затем ее разбор ниже.
$ vi data/postgresql.conf
listen_address = '*'
shared_preload_libraries = 'bdr'
wal_level = logical
wal_senders = 4
max_replication_slots = 4
track_commit_timestamp = on
bdr.connections = 'vm12'
bdr.vm12_dsn = 'host=192.168.122.12 port=5432 user=postgres dbname=staging_db'
bdr.vm12_init_replica = on
bdr.vm12_replica_local_dsn = 'host=127.0.0.1 port=5432 user=postgres dbname=staging_db'
Настройка второго узла отличается немногим, в частности здесь в конфигурации BDR мы указываем необходимость выполнить инициализацию реплики (bdr.vm12_init_replica) с узла указанного в bdr.vm12_dsn на локальную базу чьи реквизиты указаны в bdr.vm12_replica_local_dsn. Последний параметр является обязательным в случае если кластер БД инициализрован с помощью initdb (как раз наш случай) и в таком случае в кластере должна существовать пустая база которая будет в дальнейшем участвовать в репликации.
В случае инициализации через pg_basebackup опция bdr.vm12_replica_local_dsn не нужна.
Теперь определяем настройки аутентификации на обоих узлах, в моем случае все разрешено. Для production инсталяции так конечно же делать нельзя.
$ vi data/pg_hba.conf
host all all 192.168.122.0/24 trust
host replication postgres 192.168.122.0/24 trust
Выполняем перезапуск обоих узлов и смотрим журналы
$ pg_ctl -l logfile -D data/ -w restart
upstream мастер:
vm12 ~ $ tail -f logfile
LOG: unexpected EOF on standby connection
LOG: starting logical decoding for slot bdr_16384_6029905891437956874_1_16384__
DETAIL: streaming transactions committing after 0/1898F90, reading WAL from 0/1898C30
LOG: logical decoding found consistent point at 0/1898C30
DETAIL: running xacts with xcnt == 0
LOG: starting background worker process «bdr (6029905879776466735,1,16384,): vm13: apply»
LOG: unexpected EOF on standby connection
LOG: starting logical decoding for slot bdr_16384_6029905891437956874_1_16384__
DETAIL: streaming transactions committing after 0/1898F90, reading WAL from 0/1898C30
LOG: logical decoding found consistent point at 0/1898C30
DETAIL: running xacts with xcnt == 0
LOG: starting background worker process «bdr (6029905879776466735,1,16384,): vm13: apply»
downstream мастер:
vm13 ~ $ tail -f logfile
LOG: registering background worker «bdr (6029905891437956874,1,16384,): vm12: apply»
LOG: starting background worker process «bdr (6029905891437956874,1,16384,): vm12: apply»
LOG: logical decoding found consistent point at 0/18A4290
DETAIL: running xacts with xcnt == 0
LOG: exported logical decoding snapshot: «0000071B-1» with 0 xids
LOG: starting logical decoding for slot bdr_16384_6029905879776466735_1_16384__
DETAIL: streaming transactions committing after 0/18A42C8, reading WAL from 0/18A4290
LOG: logical decoding found consistent point at 0/18A4290
DETAIL: running xacts with xcnt == 0
LOG: registering background worker «bdr (6029905891437956874,1,16384,): vm12: apply»
LOG: starting background worker process «bdr (6029905891437956874,1,16384,): vm12: apply»
LOG: logical decoding found consistent point at 0/18A4290
DETAIL: running xacts with xcnt == 0
LOG: exported logical decoding snapshot: «0000071B-1» with 0 xids
LOG: starting logical decoding for slot bdr_16384_6029905879776466735_1_16384__
DETAIL: streaming transactions committing after 0/18A42C8, reading WAL from 0/18A4290
LOG: logical decoding found consistent point at 0/18A4290
DETAIL: running xacts with xcnt == 0
В журналах все хорошо и ERROR сообщений нет (а если есть, проверяйте конфиги или грешите на разработчиков))). На этом настройка и запуск завершен. Теперь можно проверить работу через создание таблиц в обеих базах.
Еще пара моментов. Временная остановка репликации осуществляется выключением downstream мастера. Однако стоит отметить что остановленная реплика приводит к тому что upstream мастер продолжит накапливать WAL журналы что в свою очередь может привести к неконтролируемому расходу пространства на диске. Поэтому крайне не рекомендуется надолго выключать реплику.
Удаление реплики навсегда осуществляется через удаление конфигурации BDR на downstream сервере с последующим перезапуском downstream мастера. Затем нужно удалить соответствующий слот репликации на upstream мастере с помощью функции pg_drop_replication_slot('slotname'). Доступные слоты можно просмотреть с помощью функции pg_get_replication_slots().
В качестве заключения скажу свои впечатления… У меня конечно есть некоторые вопросы по эксплуатации BDR, ответы на которые, скорей всего придется выяснять опытно-экспериментальным путем. Но уже на данном этапе мне нравится этот новый инструмент, настраивается оно легко и быстро, плюс к этому оно уже работает несмотря на то, что официально появится только в 9.5 (а это примерно через год). Таким образом, с релизом добавится еще один инструмент с помощью которого можно будет создавать надежные отказоустойчивые конфигурации, и это прекрасно. PostgreSQL от релиза к релизу становится только лучше и лучше.
Собственно на этом все. Всем спасибо за внимание.
P.S. Ссылки на почитать:
BDR User Guide
Logical Log Streaming Replication
PostgreSQL WAL Shipping and Streaming Replication