PostgreSQL 10 beta: логическая репликация в ядре и declarative partitioning
Тестируем beta PostgreSQL 10: встроенная логическая репликация позволяет реализовать zero-downtime миграцию между кластерами без сторонних расширений. Ждём релиза.
PostgreSQL 10 Beta - логическая репликация встроена в ядро, декларативное партиционирование, поддержка ICU collations
PostgreSQL 10 вышел в beta в мае, и мы наконец добрались до нормального стенда. Главное что хотелось пощупать - логическая репликация прямо в ядре, без pg_logical и прочих расширений. Если коротко: работает, и это меняет практику миграций между кластерами.
Что за зверь - логическая репликация в 10
До PostgreSQL 10 если нужна была логическая репликация - шёл разговор про расширение pg_logical или Londiste от Skytools. Оба рабочие, но добавляют зависимость: надо ставить, обновлять отдельно от PostgreSQL, следить за совместимостью версий. На managed кластерах это дополнительный операционный груз.
В 10 появились два нативных объекта: PUBLICATION на источнике и SUBSCRIPTION на приёмнике. Источник публикует изменения из WAL в логическом формате - строки с операциями INSERT/UPDATE/DELETE. Приёмник подписывается и применяет их на своей стороне.
Принципиальный момент: это не замена streaming replication для HA. Это инструмент другого класса - избирательная репликация отдельных таблиц, репликация между разными мажорными версиями PostgreSQL, построение read-реплик для специфических нагрузок без полного копирования всей базы.
Что тестировали
Сделали три стенда.
Первый - zero-downtime upgrade между мажорными версиями. Взяли PostgreSQL 9.6 как источник и 10 beta как приёмник. Настроили publication на источнике на несколько таблиц, subscription на приёмнике. Дали нагрузку, подождали синхронизации, потом переключили приложение. Переключение заняло время на уровне нескольких секунд - пока сливается отставание, которое накопилось за время переключения DNS.
Это именно то чего не хватало. Стандартный pg_upgrade требует остановки источника и часов даунтайма на больших базах (или возни с --link, но там свои ограничения). Логическая репликация позволяет поднять новый кластер параллельно, синхронизировать его, и переключить приложение с минимальным окном.
Второй - реплика для аналитики. На одной из клиентских баз есть паттерн: тяжёлые аналитические запросы конкурируют с OLTP-нагрузкой на одном инстансе. Streaming-реплика не подходила - нужна только подмножество таблиц, остальное не нужно реплицировать. С publication на конкретный список таблиц это решается прямолинейно.
Третий - репликация между двумя 10-ками для обкатки процедуры. Без хитростей, просто чтобы понять механику и типичные ошибки.
Декларативное партиционирование
Второй большой блок в 10 - PARTITION BY RANGE/LIST прямо в CREATE TABLE. До этого в PostgreSQL партиционирование было через table inheritance и ручные триггеры маршрутизации - работало, но процедурно, и хранить логику партиционирования в коде триггеров было удовольствием ниже среднего.
Теперь:
CREATE TABLE measurements (
sensor_id int,
ts timestamptz,
value float
) PARTITION BY RANGE (ts);
CREATE TABLE measurements_2017_q3
PARTITION OF measurements
FOR VALUES FROM ('2017-07-01') TO ('2017-10-01');
Маршрутизация INSERT-ов происходит автоматически. Старые таблицы на inheritance никуда не деваются - миграция не требуется, обе схемы сосуществуют.
На стенде погоняли INSERT и SELECT по партиционированной таблице с восемью партициями по кварталам. Constraint exclusion работает как ожидается - запросы с условием по ts сканируют только нужные партиции. Плюс в планах EXPLAIN это теперь читается как нормальный SQL, без артефактов inheritance-подхода.
Ограничения beta: UPDATE между партициями (когда строка переходит из одной партиции в другую) не работает - вернёт ошибку. По документации это ограничение первой реализации. Для временных рядов где UPDATE по ключу партиционирования редкость - некритично.
ICU collations
Небольшая, но реальная улучшайзинга. PostgreSQL 10 добавляет поддержку ICU (International Components for Unicode) как провайдер collation, в дополнение к системному libc. Это важно для русского языка - libc-сортировка на разных дистрибутивах вела себя по-разному в зависимости от версии glibc, и это создавало головную боль при сравнении индексов после обновления ОС. ICU collations детерминированы вне зависимости от системных библиотек.
Не тестировали подробно - взяли на заметку как тему для отдельного разбора.
Что нашли неприятного
Beta есть beta. Пара моментов которые заставили почесать голову.
Первичная синхронизация при subscription. При создании SUBSCRIPTION PostgreSQL копирует начальное состояние таблиц через COPY. На больших таблицах это долго и блокирует вакуум на источнике на время синхронизации. На наших тестовых объёмах - приемлемо. На реальных базах с таблицами в сотни гигабайт надо закладывать окно.
Нет DDL-репликации. Логическая репликация реплицирует данные, не структуру. Если добавить колонку на источнике - на приёмнике надо добавить вручную. Это осознанное архитектурное решение, но в сценарии с миграцией версий это значит: schema migration надо делать координированно на обеих сторонах.
Конфликты при двусторонней записи. Если писать на обе стороны одновременно - получишь конфликты, которые нужно разрешать вручную или через pg_replication_origin. Для сценария «читать с реплики» - не проблема. Для чего-то более сложного - нужна дисциплина.
Итог
Четыре-пять лет работы с PostgreSQL показывают что мажорные версии выходят стабильными - 9.4, 9.5, 9.6 все ввели в продакшн без неприятных сюрпризов после GA. PostgreSQL 10 beta выглядит так же: нет ощущения что там что-то принципиально сырое.
Логическая репликация в ядре - это наконец рабочий инструмент для zero-downtime миграций без дополнительных зависимостей. На клиентских managed-инфраструктурах это меняет подход к обновлению мажорных версий: вместо «планируем даунтайм» - «планируем окно переключения».
Ждём GA. Процедура готова, стенд отработан.