PostgreSQL 10 GA: логическая репликация stable и декларативное партиционирование в работе
Обновили первый кластер до PostgreSQL 10 GA. Декларативное партиционирование по диапазону дат закрыло самодельные триггерные схемы и сразу ускорило запросы к историческим таблицам.
PostgreSQL 10 GA - логическая репликация stable, декларативное партиционирование, quorum commit для синхронной репликации
PostgreSQL 10 вышел GA 5 октября. Мы тестировали beta с августа, процедура обновления была отработана на стенде, поэтому первый кластер перевели на 10 почти сразу - в течение двух недель после релиза. Вот что получилось на практике.
Зачем торопиться
Честно: не торопились. Просто этот кластер был хорошим кандидатом. Там хранятся исторические данные телеметрии - таблицы по несколько сотен гигабайт, партиционированные по месяцам через старую схему с table inheritance и ручными триггерами маршрутизации. Декларативное партиционирование из PostgreSQL 10 было написано как будто специально под эту ситуацию.
Логическая репликация нам была нужна именно для самого обновления - поднять новый кластер на 10, реплицировать данные, переключить приложение без остановки.
Обновление через логическую репликацию
Схема, которую отработали на стенде в августе, сработала на продакшне без сюрпризов.
Источник - PostgreSQL 9.6. Приёмник - чистый PostgreSQL 10. Создали publication на источнике на основные рабочие таблицы, подняли subscription на приёмнике. Дали ему догнать источник - на нескольких сотнях гигабайт первоначальная синхронизация через COPY заняла несколько часов. Подождали, пока лаг репликации упал до нуля, потом переключили приложение.
Окно переключения - время пока сливается отставание, которое накопилось за переключение DNS и пул соединений. На нашем трафике это заняло меньше минуты. Никакого планового даунтайма, никакой ночной остановки сервиса.
Один момент который потребовал аккуратности: DDL не реплицируется. Схему на приёмнике надо держать синхронной вручную. На этапе подготовки прогнали все pending миграции заранее, перед настройкой репликации. Если бы между настройкой репликации и переключением прилетела бы ещё одна миграция - надо было бы её накатить на оба кластера скоординированно. Обошлось, но на будущее этот момент надо держать в процедуре явно.
Декларативное партиционирование: что реально изменилось
Это оказалось важнее логической репликации для конкретно этого кластера.
Старая схема выглядела так. Родительская таблица, дочерние таблицы через INHERITS, триггер BEFORE INSERT на родителе, который смотрит на дату и делает INSERT INTO measurements_YYYY_MM VALUES (NEW.*); RETURN NULL. Это работало. Но:
- триггер жил в отдельном объекте, его легко было забыть при изменении структуры таблицы;
- добавление новой партиции значило: создать таблицу, добавить constraint, обновить триггер;
- explain по запросам через родительскую таблицу выдавал планы с appendами по всем дочерним таблицам, даже когда constraint exclusion должен был их отсечь - иногда планировщик ошибался.
Новая схема. Дропнули триггер. Пересоздали родительскую таблицу как PARTITION BY RANGE (ts). Создали партиции через PARTITION OF ... FOR VALUES FROM ... TO. Данные исторические, поэтому партиции создавали заново через bulk insert - на телеметрии это была просто перекачка из старых дочерних таблиц в новые.
Маршрутизация INSERT-ов работает автоматически. Добавление новой партиции - одна команда CREATE TABLE. Никакого триггера, никакой процедуры которая может разъехаться с реальностью.
По запросам к историческим данным - там где раньше планировщик иногда сканировал лишние партиции - стало заметно лучше. Запросы к конкретному временному диапазону ускорились примерно на 40% по p95 времени ответа. Это не только partition pruning - мы одновременно переиндексировали несколько таблиц, так что цифра не чистая, но основной вклад именно от новой схемы партиционирования.
Quorum commit для синхронной репликации
На этом кластере синхронная репликация не используется, так что quorum commit мы пока только изучили на уровне документации. Идея хорошая: вместо synchronous_standby_names = 'replica1' (один отстал - транзакции висят) можно задать ANY 1 (replica1, replica2, replica3) - достаточно подтверждения от любого одного из трёх. Практически это позволяет держать несколько реплик и не уходить в проблему single point of dependency на одну реплику.
Попробуем на кластере где синхронная репликация реально нужна.
Что осталось неизменным
Логическая репликация по-прежнему не реплицирует DDL и не поддерживает двустороннюю запись без ручного разрешения конфликтов. Это не баги, это архитектурные решения - просто надо держать в голове при проектировании схемы использования.
Обновление на managed инфраструктуре прошло штатно. Процедура, которую отработали на стенде в августе, на продакшне не потребовала импровизации - именно за это и стоит тратить время на стенды.
Следующий кластер на очереди тот где используется синхронная репликация - там будет повод проверить quorum commit в деле.