ADG Оставить заявку
Блог Данные и аналитика 4 мин чтения

PostgreSQL 9.1 streaming replication: тестируем как замену SQL Server Standard

PostgreSQL 9.1 вышел с синхронной потоковой репликацией и foreign data wrappers. Тестируем synchronous_standby для нулевой потери данных и мониторим lag через Zabbix.

Контекст момента

PostgreSQL 9.1 вышел в сентябре 2011 с синхронной потоковой репликацией, synchronous_standby_names и foreign data wrappers

У одного из клиентов стоит SQL Server Standard Edition. Две лицензии - на основной сервер и на standby, который держится исключительно ради HA. Каждый год продление подписки, и каждый год разговор с финансовым директором об одном и том же: «а почему нельзя что-то бесплатное?». Ответ раньше был прост: бесплатные базы не дают нормальной синхронной репликации с гарантией нулевой потери данных. PostgreSQL до версии 9.0 умел только асинхронный streaming replication - и то только в 9.0, до этого вообще только через PITR-архивы.

PostgreSQL 9.1 вышел в сентябре. Главное, что нас интересовало: synchronous_commit = on плюс synchronous_standby_names. Это уже не «примерно в реальном времени», а честная синхронная репликация - транзакция подтверждается клиенту только после того, как WAL-запись получена и подтверждена standby. То есть при сбое primary никаких потерянных транзакций нет по определению.

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

Как устроена синхронная репликация в 9.1

Архитектура в общих чертах:

  • Primary пишет WAL-сегменты и отправляет их на standby через WAL sender process в реальном времени.
  • Standby принимает через WAL receiver, применяет через recovery.
  • При synchronous_commit = on primary ждёт ACK от standby перед тем, как вернуть клиенту успех транзакции.
  • synchronous_standby_names задаёт имя (или список) standby, которые считаются синхронными.

Если синхронный standby недоступен - primary продолжает работать, но переключается в асинхронный режим и ждёт возврата standby. Это поведение настраивается. Важная деталь: в 9.1 можно указать только один синхронный standby. Несколько - уже другой разговор.

postgresql.conf на primary получился примерно такой:

wal_level = hot_standby
max_wal_senders = 3
synchronous_commit = on
synchronous_standby_names = 'standby1'

На standby - recovery.conf с standby_mode = on, primary_conninfo и trigger_file для промоута.

Что проверяли

Первое - задержка. При синхронной репликации каждый COMMIT ждёт сетевого round-trip до standby. На нашем тестовом стенде в одном датацентре (latency между хостами меньше миллисекунды) overhead практически неощутим - транзакции на OLTP-нагрузке чуть медленнее, но в пределах погрешности. Если standby в другом здании через WAN - ситуация другая, и там уже надо думать о synchronous_commit = local или асинхронном режиме.

Второе - failover. Убили primary. На standby вручную создали trigger-файл. Standby промоутировался в primary примерно за 10-15 секунд. Никаких потерянных транзакций - проверили по счётчику в тестовой таблице, который писали до момента убийства primary. Всё на месте.

Третье - lag под нагрузкой. При интенсивной записи смотрели на pg_stat_replication - там есть sent_location, write_location, flush_location, replay_location. В синхронном режиме flush_location на standby догоняет primary почти мгновенно. Разрыв появлялся только при искусственном замедлении сети.

Мониторинг lag через Zabbix

Когда мы переезжали на Zabbix весной, настроили его на всё что движется. PostgreSQL репликацию тоже хочется видеть. pg_stat_replication отдаёт позиции в формате LSN (log sequence number), разницу можно получить через pg_xlog_location_diff(). На primary завели UserParameter:

UserParameter=pgsql.replication.lag,psql -U zabbix -t -c "SELECT COALESCE(pg_xlog_location_diff(pg_current_xlog_location(), flush_location), 0) FROM pg_stat_replication WHERE application_name='standby1'"

Значение в байтах. Тригер на lag больше 1 МБ - это уже повод смотреть, что происходит со standby. На тестовом стенде lag стабильно держался в нуле при синхронной репликации - что и следовало ожидать, так и должно работать.

Foreign data wrappers - неожиданный бонус

В 9.1 появился SQL/MED и первый внятный FDW - postgres_fdw для подключения к другому PostgreSQL-серверу как к внешней таблице. Для задач работы с данными и аналитики это интересная штука: можно тянуть данные из нескольких источников прямо в запросе, без ETL. Пока это выглядит как proof-of-concept - производительность зависит от того, как оптимизатор умеет проталкивать предикаты во внешний запрос, а это отдельная тема. Но сам механизм стандартный (SQL/MED), что внушает доверие.

Что в итоге

Для конкретного клиента и его нагрузки PostgreSQL 9.1 с синхронной репликацией выглядит как реальная альтернатива SQL Server Standard в части HA. Failover ручной - trigger-файл не создаётся сам по себе. Автоматического промоута нет, это придётся делать скриптом или через Pacemaker. Но потеря данных исключена, а это было главным требованием.

Сейчас готовим предложение для клиента - показываем тесты, считаем стоимость миграции и поддержки. SQL Server никуда не денется сразу: часть задач завязана на SSIS и SSRS, которые PostgreSQL не заменит. Но standby-лицензия - кандидат номер один на выбывание.

Если кто-то уже гонял PostgreSQL 9.1 на продакшн-нагрузке с синхронной репликацией - интересно услышать про edge cases.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.