Одна из открытых пока задач в области баз данных - поддержание базы данных в консистентном состоянии одновременно на нескольких экземплярах СУБД (узлах), принимающих клиентские соединения независимо друг от друга. Суть проблемы заключается в том, что в случае отказа одного из узлов такой системы остальные должны продолжить свою работу без перерыва: принимать соединения, коммитить транзакции не потеряв при этом консистентность. Аналогией для случая одного экземпляра СУБД здесь может быть, к примеру, обеспечение работы при отказе планки оперативной памяти или прерывающемся доступе к нескольким ядрам процессора.
Здесь я хочу возобновить обсуждение проблемы мультимастера на базе Postgres: его практическую ценность, реализуемость, стэк технологий, которые нужно создать. Возможно, что поставив проблему более узко, мы сможем прийти к решению, которое будет полезно для отрасли...
Я занимался созданием мультимастера несколько лет вплоть до того момента когда стало понятно, что концепция essentially consistent multimaster зашла в тупик. Сейчас, после длительного перерыва, сменив компанию я делаю ещё один заход на идею постгрессового мультимастера с целью таки закрыть гештальт и найти ему практическое применение (или, кто знает, показать что это нереализуемо).
Для начала, хочется определиться собственно с общей моделью его использования, в каком виде мультимастер сможет приносить пользу. Понятно, что любая технология - это баланс, между возможностями и потребностями. Давайте же попробуем нащупать этот баланс в мультимастере.
Обычно, клиенты приходят к идее мультимастера, когда доходят до предела возможностей по количеству подключений в OLTP-нагрузке. У них есть большое количество клиентов, N tps производительность системы, одна база данных. Их идея заключается в том, чтобы поставить рядом ещё один такой же сервер, настроить active-active-репликацию и обеспечить себе 2xTPS нагрузку.

Бывает же, что клиент имеет распределенное приложение и хочет, чтобы коннект этого ��риложения к базе был стабилен и примерно одинаков в разных географических точках. А иногда люди приходят просто за надежным автоматическим фейловером. Гораздо более редкая задача (а скорее бонус) - обеспечить онлайн апгрейд, или плавный вывод одного из узлов на обслуживание. А еще бывает запрос на дополнительный мастер, имеющий прогретые кэши и состояние storage, близкое к боевому, который можно использовать в качестве тестового стенда … В общем задач хватает, однако что из этого в реальности можно обеспечить?
Следует помнить, что active-active репликация в Postgres возможна пока только на логической репликации - а это значит сетевые задержки, дополнительная нагрузка на сервер от декодинга, walsender’a и т.д. Ну и сразу появляются сетевые задержки - нам же нужно дождаться подтверждения от удалённой ноды, что транзакция была применена успешно, так? Поэтому идея масштабировать пишущую нагрузку для общего случая 100% репликации сразу упирается в тот факт, что каждый сервер обязан будет писать не только свои изменения, но ещё и изменения, приходящие с других инстансов (см. рисунок выше). Это не страшно для больших транзакций с развесистыми SELECT-ами, однако запрос на мультимастер скорее подразумевает клиентов с OLTP и очень простым DML.
Примерно с той же проблемой мы сталкиваемся, когда хотим просто обеспечить экземпляр распределенного приложения рядом стоящей базой с устойчивым коннектом. Ведь если коннект приложения с удаленной базой был плох, то и коннект двух экземпляров СУБД друг с другом также будет нестабилен. А раз так, то необходимость дожидаться подтверждения успешного коммита транзакции на удаленной ноде также потребует длительного времени ожидания.
А ещё, могут возникать логически непростые ситуации с рапределёнными конфликтами, когда приходящее с репликацией обновление пытается перезаписать ту же строку таблицы, что и локальное - ведь не факт, что снапшот транзакций, вызвавших эти конфликтующие изменения, согласован на разных инстансах. Тогда чьё изменение должно быть применено, а чьё нужно откатить? Должно ли одно изменение строки, в логике транзакции, знать о конкурирующем изменении, чтобы такое изменение было корректно?
С автофейловером ситуация проще, но и здесь нужно что-то изобретать, чтобы оставаться эффективным: ведь если все инстансы могут писать, то на коммит транзакции необходимо добавлять ожидание подверждения записи транзакции на каждом инстансе - иначе может статься, что при падении узла, часть его транзакций будет записана в базу на узле
, но не успеет попасть (или попадёт под rollback) в базе узла
. И как тогда исправлять подобную ситуацию - отправлять всю конфигурацию в recovery?
Итак, мы видим, что идея мультимастера в идеальном случае выглядит сомнительно. По крайней мере для тех, кто хочет получить от него ускорение OLTP-транзакций. Тогда для чего он может быть нужен вообще? Попробуем оттолкнуться от базовой технологии - логической репликации.
Я вижу два существенных достоинства логической репликации. Во-первых, она позволяет реплицировать данные селективно, выбирая конкретные таблицы. Также, для каждой таблицы можно настроить фильтр с условиями репликации, которые позволят быстро пропускать отдельные записи и целые транзакции ещё на самом первом этапе репликации - на этапе декодинга. Получаем весьма точечный механизм, позволяющий выделить конкретные данные, которые должны быть синхронизированы с удалённой стороной. Второе любопытное достоинство - высокоуровневость механизма. Фактически, репликация происходит на уровне реляционной алгебры, а значит, можно абстрагироваться от деталей физического хранения.
Что даёт высокий уровень абстракции? - например, можно иметь различный набор индексов на синхронизируемых узлах, локально уменьшая накладные расходы на DML и перенаправлять запросы в соответствии с тем, где выполнение может быть эффективнее: нагружать один инстанс короткими точечными UPDATE'ами/DELETE'ами по первичному ключу, оставляя DML с развесистыми подзапросами (или обычно не конфликтующие с обновлениями INSERTы) другому инстансу. Также, можно использовать стандартный Postgres heap на одном и колоночный storage на другом. Или вообще попытаться использовать разные СУБД в качестве узлов мультимастера - фантазия ограничена только возможностями протокола репликции!
Теперь, определившись с преимуществами логической репликации, можно представить себе сценарий использования, который может быть наиболее эффективно реализован мультимастером.
Для начала, отставим в стороне запрос на апгрейд, обслуживание и фейловер. Самым очевидным кейсом является обеспечение работы геораспределенного приложения. И здесь можно получить преимущества, если классифицировать данные в базе на критически важные общие, общие с записью только на одной стороне и сугубо локальные (см. рисунок).

Здесь красный цвет обозначает данные, которые должны быть надёжно синхронизированы между инстансами, зелёный и синий - те, которые не требуют немедленной синхронизации и зачастую доступны противоположной стороне по чтению и серый - сугубо локальные данные.
Спроектировав схему БД так, чтобы классифицировать данные по методу репликации мы можем даже уменьшить размер базы данных на конкретном инстансе, не отправляя локальные данные на удалённые узлы. Также, большАя часть данных может реплицироваться в одну сторону асинхронным методом, избегая оверхеда на ожидании подтверждения коммита от удалённой стороны. И только для критически важной части данных нужно обеспечить строгую синхронизацию, задействуя такие механизмы как синхронный коммит, 2PC и правила видимости не ниже REPEATABLE READ, что существенно просаживает время коммита такой транзакции и повышает риск отката вследствие конфликта.
Какой пример может быть у такого варианта использования? Я не имею опыта инсталляций у заказчика, поэтому могу представлять себе, как оно может быть чисто гипотетически. Это может быть международная компания, у которой данные о сотрудниках, равно как финансовые показатели должны храниться на серверах внутри каждой отдельной страны (вроде бы нынче это типовое требование). При этом в целях аналитики на чтение они могут быть доступны и снаружи - это похоже на кейс с фильтрацией реплицируемых данных по значению ключа. Также, таблицу сотрудников компании можно расщепить, реплицируя имена, должности, зарплаты и т.д. по всем экземплярам БД, вынося в отдельную локальную таблицу идентифицирующие конкретную личность номер соц. страхования или паспорта.
В принципе, если в спроектированной таким образом БД основная нагрузка будет ложиться на локальные и асинхронно реплицируемые данные, то можно даже добиться так желаемого масштабирования по записи (звучит еретически, но кто знает ...).
Из опыта работы в ракетостроении я вынес привычку оценивать изучаемый эффект качественно. Попробуем прикинуть, какой процент базы может реплицироваться в режиме active-active, чтобы потенциально производительность не проседала. Пусть, для простоты, у нас имеется два филиала компании на двух континентах и варианты конфигурации: (1) один сервер и (2) два сервера в режиме мультимастер, для которых доступ всегда будет локален (см. рисунок ниже).

Введём некоторые обозначения (см. также на рисунке):
время выполнения транзакции в бэкенде СУБД, мс.
network round-trip time для приложения, расположенного рядом с экземпляром СУБД, мс.
network round-trip time для приложения, расположенном на другом, относительно СУБД, континенте, мс.
время, необходимое для получения подтверждения о факте успешного коммита с удалённого экземляра СУБД, мс.
доля локальных подключений.
N - доля транзакций с DML, которым достаточно асинхронной репликации.
Для случая с одним сервером имеем:
.
Для мультимастера имеем:
.
Теперь зададимся подходящими числами для наших формул. Примем долю 50% для локальных подключений (). В качестве характеристики сетевого подключения, по опыту проживания в Азии и коннекта к московским ресурсам позвольте принять следующие цифры:
Здесь и
- это время одного раунд-трипа, в то время как, для подтверждения удалённого коммита
будем считать, что потребуется два раунд-трипа: в 2PC-протоколе сначала следует выполнить PREPARE STATEMENT, дождавшись успешной репликации изменений и резервирования всех необходимых для коммита ресурсов, а потом уже посылать команду COMMIT.
Задавшись такими цифрами мы получаем:
.
А теперь, представим, что количество удалённых подключений у нас выросло до 80%:
.
А что, если нужно обеспечить полную синхронную 2PC-синхронизацию всей базы данных? Посчитаем: ,
. Получается потеря производительности в
раза для позитивного сценария. Не очень перспективно, но может кому-то этого вполне достаточно?
Такой грубый расчёт приводит нас к выводу, что при примерно 25% транзакций с DML, которым требуется подтверждение на удаленной стороне, мультимастер не почувствует просадки производительности. А в случае, когда большая часть трафика приходит откуда-то из удалённых регионов, то доля надёжно реплицируемых данных может достигать 40%, но будем консервативны и будем ориентироваться на N = 25%. При этом, мы снимаем половину нагрузки на дисковую подсистему, локи и прочее, что может быть использовано в локальных операциях, таких как вакуум, или в read-only запросы. Кажется, здесь появляется рациональное зерно, нет?
Оборотная сторона медали заключается в том, что репликация, даже асинхронная, должна потенциально не отставать от потока коммитов. Очевидно, что если полное время на локальное выполнение транзакции составляет 15 мс, а one-way delay до удаленного сервера - 75 мс, то даже если не ждать подтверждения с той стороны, то в последовательном случае очередь изменений для репликации будет копиться.
25% DML в нашей задаче коммитятся с подтверждением на удаленной стороне, значит остается 75%. . Чтобы покрыть эту разницу между темпом локальных коммитов и скоростью передачи данных на удаленный сервер, необходимо воспользоваться шириной канала: отправлять, а на удалённом сервере принимать данные параллельно (т.е. нужна параллельная репликация). И в нашей грубой модели выходит так, что передавать изменения достаточно в четыре потока. С учётом высвободившихся на сервере ресурсов (от распределения коннектов между инстансами) выглядит достаточно реалистично.
Итого, в сухом остатке имеем, что разнося географически данные в режиме мультимастера мы в теории можем рассчитывать на сравнимый TPS. При этом уменьшается количество бэкендов и потребление ресурсов на каждом конкретном сервере. Эти ресурсы могут быть высвобождены под системные процессы и различную аналитику. Не забудем и про возможность оптимизации индексов, storage и прочих атрибутов физического размещения данных на каждом узле системы независимо. Дополнительный бонус заключается в том, что в случае нарушения связи можно будет обеспечить врЕменную работу каждой из подсетей с расчетом, что после восстановления та или иная стратегия разрешения конфликтов поможет восстановить целостность базы.
Несложно представить себе и приложение для такого кейса с полным развалом сети. Например - база данных для сети больниц где-нибудь в Юго-восточной Азии - регионе со сложным рельефом и климатом. База мед записей должна быть общая, но один клиент врядли за короткий срок будет обслуживаться в двух разных больницах, что обеспечит редкость ситуации конфликта в критически важных данных.
Учитывая все вышесказанное, мультимастер, который может найти себе применение, должен иметь на борту следующий набор технологий:
Replication sets - чтобы классифицировать данные для репликации, отдельно обеспечивать синхронную и асинхронную репликацию.
Коммит с подтверждением (по аналогии с 2PC).
Протокол распределенного консенсуса для определения работоспособного подмножества нод и фенсинга сбоящих нод в 3+ конфигурациях.
Параллельная репликация - распараллеливание как отправки DML, так и их применения на удалённой стороне.
Automatic Conflict Resolution для автономного режима работы.
Не забудем также автоматический фейловер и возможность горячей замены оборудования без остановки. Возможность альтернативной физической организации хранения данных выглядит пока несколько фантастически и я оставляю её за скобками. Однако, если появится опыт успешного применения мультимастера, то может быть эта опция станет в какой-то момент основной?
Давайте теперь посмотрим, что из вышеназванного реализуют текущие известные проекты мультимастера:
Любопытно, что пока ни один проект не реализовал наш базовый стэк технологий. Интересно, а позволяет ли вообще архитектурно какой-то из этих проектов реализовать все потребные мультимастеру фичи?
На этом всё. Поскольку публикация задумана для затравки дискуссии, не сомневайтесь оставлять свое мнение в комментариях или любым другим удобным способом.
Ссылки
THE END.
Испания, Мадрид, 15 октября 2025 года.