
В современных реалиях объёмы данных постоянно растут, и появляются всё более жёсткие требования к производительности. Тут традиционный PostgreSQL сталкивается с фундаментальной проблемой: отсутствием нативной поддержки горизонтального масштабирования.
Сегодня мы, команда платформы данных в Yandex Cloud, хотим рассказать о SPQR — нашем опенсорс‑инструменте, который создавался как ответ на «боль» шардирования и эксплуатации крупных OLTP‑систем. Под катом — история о том, что стало отправной точкой для его создания, какие задачи он помогает решать, на чём основано наше решение и что позволяет ему быть довольно простым в эксплуатации.
Почему шардирование для PostgreSQL — это боль
PostgreSQL — отличная база данных: популярная, расширяемая. Однако у неё есть фундаментальное ограничение — «из коробки» она не поддерживает горизонтальное масштабирование. Многие сервисы рано или поздно достигают пределов одного экземпляра Postgres, после чего начинается «танец с бубном». При достижении одной базой данных объёма в несколько терабайт и с нагрузкой более 100 000 запросов в секунду (QPS) вертикальное масштабирование перестаёт быть эффективным.
Типичный подход к горизонтальному масштабированию — разделение таблиц по ключам и установка рядом с приложением (или приложениями) специального координатора, который знает, на какой шард направить запрос. Однако у такого подхода есть несколько серьёзных проблем:
Сложные миграции. Вернуться из монолита к кластерам и обратно без простоя почти невозможно.
Не хватает готовых инструментов. Vitess отлично работает с MySQL, но попытки привнести его в Postgres оказались слишком трудоёмкими. К этому времени многие компании (в том числе Яндекс) писали собственные мини‑фреймворки для роутинга. В целом это странная ситуация: у MySQL есть готовое решение, а у Postgres нет.
Проблемы с балансировкой и переносом данных. Без автоматизации дежурным приходилось ночью переносить «горячие» ключи вручную, чтобы избежать переполнения или перегрузки отдельных шардов. Шардов может быть сотни, и любая ошибка грозит отказом.
Метаданные не масштабируются. При хранении сведений о каждом ключе метаданные быстро разрастаются и перестают помещаться в один экземпляр Postgres, становясь новым узким местом системы.
Инженеры из Data Platform Yandex Cloud решили, что мириться с этими недостатками нельзя. Так появилась система SPQR (Stateless Postgres Query Router) — опенсорс‑решение для горизонтального масштабирования Postgres, оптимизированное под OLTP‑нагрузки и плавные миграции.
Как работает SPQR
Мы не сразу пришли к текущему дизайну SPQR. Сначала были эксперименты с FDW‑подходом (Foreign Data Wrapper) — классическим способом связать несколько PostgreSQL‑инстансов через внешние таблицы. Это выглядело естественным: минимум внешнего кода, можно использовать стандартный планировщик запросов. Но довольно быстро выяснилось, что при большом числе шардов и высоком QPS такая схема даёт слишком большие накладные расходы.
Следующим шагом стала попытка реализовать CustomNode‑based sharding — расширение на C, которое встраивалось в планировщик Postgres. Производительность улучшилась, но не было ясно, что делать с апгрейдом мажорных версий PostgreSQL. В итоге мы решили написать proof‑of‑concept proxy‑решения — и у нас получилось!
Роутер вместо кастомных драйверов
Главная идея SPQR — поставить между приложением и шардами лёгкий прокси‑роутер. Приложения подключаются к нему по обычному протоколу PostgreSQL, «не догадываясь», что работают не с базой данных, а с Golang‑приложением. Такой подход позволяет на уровне роутера перенаправлять запросы, распределять их между репликами, не модифицируя код приложения.

Роутер анализирует первый запрос в транзакции и решает, на какой шард отправить транзакцию целиком. SPQR поддерживает как одноколоночные шард‑ключи, так и композитные ключи. При необходимости разработчик может указать ключ явно в SQL‑комментарии, например:
INSERT INTO orders(id, data) VALUES (10, '...') /*__spqr__sharding_key: 1, 100*/;
Если подходящего ключа в запросе нет, можно настроить default shard — роутер отправит транзакцию на него по умолчанию.
Координатор и QDB
Один роутер неизбежно стал бы узким местом и единой точкой отказа, поэтому система SPQR изначально спроектирована так, что можно запускать сколько угодно роутеров параллельно.

Правила шардирования хранятся в памяти каждого роутера, но синхронизируются через отдельный сервис — координатор. Координатор записывает метаданные в небольшую базу QDB (внутри работает кластер etcd) и раздаёт актуальные правила всем роутерам.

Благодаря этому можно динамически добавлять и удалять роутеры, не беспокоясь о рассогласованности конфигурации.
Решение проблемы балансировки
Одна из главных трудностей при шардировании — неравномерная нагрузка. Когда один шард перегружен, а другие простаивают, администраторам приходилось переставлять данные вручную. SPQR предоставляет балансировщик, который делает это автоматически.

Вместо того чтобы полностью копировать данные с одного шарда на другой, как предлагают многие решения на основе логической репликации, координатор SPQR делает ε‑split: «отрезает» от перегруженного диапазона маленький «кусочек» (ε) и переносит его без долгих блокировок. Этот процесс повторяется до тех пор, пока нагрузка не выровняется. Подход с ε‑split минимизирует время, в течение которого мигрируемый диапазон находится в режиме read‑only, и позволяет переносить данные даже на сильно загруженных кластерах.
Балансировщик реализован как часть координатора и расширение pg_comment_stats для шардов. Он следит за метриками шардов и по заданным стратегиям решает, когда и какие диапазоны переносить. Если на шарде выполняется транзакция, затрагивающая перевозимый диапазон, координатор дождётся её завершения и только потом заблокирует кусок и перевезёт его.
Работа с транзакциями и кросс-шардовые запросы
Прежде всего система SPQR рассчитана на single‑shard OLTP‑сценарии, поэтому кросс‑шардовые запросы поддерживаются только в ограниченном объёме. Разрешены, например, SELECT * FROM… без WHERE и DDL‑команды (CREATE TABLE и пр.) — такие запросы выполняются по принципу best effort и могут вернуть несогласованный срез данных. В продакшене этот режим отключён по умолчанию, но его можно включить, установив в конфигурации query_routing.default_route_behaviour = BLOCK.
В планах — поддержать более богатые кросс‑шардовые запросы, но это долгосрочная задача.
Для транзакций на нескольких шардах SPQR поддерживает двухфазный коммит. Специальный комментарий в запросе _spqr_commit_strategy определяет стратегию фиксации: 1pc (однофазная фиксация без координации) или 2pc (двухфазная фиксация).
В режиме 2pc изменения на всех шардах применяются атомарно, но важно понимать, что двухфазный коммит не обеспечивает полной изоляции — это принципиальное ограничение всех СУБД, использующих 2pc. То есть в межшардовых транзакциях возможны аномалии из‑за отсутствия глобального уровня snapshot isolation.
В планах — добавить поддержку CSN (Commit Sequence Number), чтобы обеспечить глобально согласованный порядок транзакций и тем самым устранить часть аномалий, присущих двухфазной фиксации.
Отказоустойчивость и интеграция без модификации СУБД
Без состояния — максимум надёжности. SPQR не хранит состояние: все правила маршрутизации лежат в QDB, поэтому число роутеров не ограничено. Это позволяет запускать сотни экземпляров роутеров одновременно, обеспечивая горизонтальную масштабируемость и отказоустойчивость.
Умная маршрутизация и отказоустойчивость шардов. Для каждого шарда можно указать несколько серверов. SPQR автоматически распределяет read‑only‑запросы по репликам и при недоступности любого из них перенаправляет трафик. Кроме того, предусмотрен режим dedicated read‑only router: в нём SPQR отвечает true на SHOW transaction_read_only и принимает только запросы на чтение — аналогично обычной реплике PostgreSQL.
Интеграция без кастомных драйверов. Приложения подключаются к SPQR по стандартному PostgreSQL‑протоколу, никаких модификаций или специальных библиотек не требуется. Для администрирования предусмотрен отдельный порт: через psql можно выполнять служебные команды вроде SHOW shards; или SHOW clients; для мониторинга состояния кластера.
Простая эксплуатация. SPQR поддерживает все типы аутентификации PostgreSQL (trust, md5, scram, ldap, gss) и имеет готовый quickstart: кластер можно запустить через Docker за пару минут. Достаточно скачать образ роутера и задать конфигурацию шардов — и полноценный тестовый шардинг‑кластер готов к работе.
Отличия от других решений
При разработке SPQR мы сформулировали несколько принципов, которые отличают систему от большинства инструментов шардирования PostgreSQL:
Использование проверенных HA‑кластеров как строительных блоков. Для каждого шарда подходят Patroni, Stolon, PgConsul или управляемые облачные сервисы. SPQR не изобретает собственную репликацию и опирается на уже существующие решения высокой доступности.
Без простоя при миграциях. Монолитная база становится первым шардом, затем добавляются новые узлы, и данные переносятся без остановки сервиса. Тем же механизмом можно «схлопнуть» шарды обратно в монолит.
Лёгкая установка. Dev‑кластеры должны подниматься на ноутбуке за минуты, а не часы. Для этих целей есть готовый Docker‑образ.
Оптимизация под OLTP. SPQR добавляет к запросу всего ~ 1–2 мс накладных расходов, что приемлемо для кратких транзакций.
Перенос данных небольшими диапазонами. Данные можно «переливать» между шардами пропорционально нагрузке. Большие диапазоны ключей автоматически разбиваются на маленькие, чтобы минимизировать время блокировок при переносе данных.
Без головной боли с лицензией. SPQR использует открытую лицензию PostgreSQL.
Кому подходит SPQR
Система SPQR рассчитана преимущественно на OLTP‑нагрузки. Она особенно эффективна, когда большинство транзакций укладывается в рамки одного шарда. Вот её основные сценарии использования:
Электронная коммерция и финтех. Интернет‑магазин можно разделить по customer_id или product_category и обрабатывать десятки тысяч заказов в секунду.
Контент‑платформы. Блоги, новостные порталы и CMS‑разработки часто шардируются по author_id или типу контента, при этом комментарии и связанный контент остаются на одном шарде.
Хранилища микросервисов. Каждый микросервис получает свой логический шард, но физически всеми данными управляет единый роутер — это упрощает архитектуру хранения без множества отдельных баз данных.
Пользовательские сервисы, IoT, high‑traffic OLTP. SPQR обеспечивает низкие задержки, поддерживает до 100 000 запросов в секунду и терабайты данных, поэтому подходит для сервисов с большим потоком коротких транзакций (онлайн‑игры, платёжные системы, телеметрия IoT и тому подобное).
При этом SPQR не пытается заменить полноценные распределённые СУБД (такие, как YDB или CockroachDB): если вам нужны строго консистентные кросс‑шардовые транзакции или сложные аналитические запросы, лучше обратить внимание на HTAP‑решения. Но если ваша нагрузка хорошо шардируется, а переписывать приложение или городить собственный роутинг не хочется, SPQR станет удобным решением.
Текущий статус проекта и планы
SPQR развивается как открытый проект на GitHub под лицензией PostgreSQL Global Development Group. Код можно свободно использовать и модифицировать без ограничений (никакого AGPL). Команда Data Platform уже использует SPQR в проде. А ещё нашим инструментом пользуются Яндекс ID, Яндекс Пэй и Едадил.
Из наших планов на будущее:
дальнейшее развитие балансировщика и метрик мониторинга;
поддержка более богатых кросс‑шардовых запросов;
развитие административного API и инструментов наблюдения за кластером.
SPQR развивается не только внутри Yandex Cloud — в проект уже активно вносят вклад внешние контрибьюторы. У нас есть несколько внешних коммитеров, которые добавляют полноценные фичи среднего масштаба — из последнего, например, поддержку default shard. Команда рада любой обратной связи: код полностью открытый и независимый — никаких enterprise features за paywall. Будем рады вашим комментариям, пул‑реквестам и звёздочкам в репозитории. А также заглядывайте на трансляцию нашего Open Source Jam — поговорим о PostgreSQL и не только.
SPQR — попытка сделать PostgreSQL по‑настоящему масштабируемым без компромиссов.
Лёгкий роутер на Go распределяет нагрузку по шардам, координатор следит за целостностью правил, а балансировщик переносит данные микропорциями, избегая долгих блокировок. Несмотря на ограничения — транзакции в основном внутри одного шарда и пока ограниченную поддержку cross‑shard‑запросов — проект уже успешно работает в продакшене и доказывает, что шардированный Postgres может быть одновременно простым и надёжным.
В отличие от «настоящих» распределённых СУБД, система SPQR не пытается во что бы то ни стало обеспечить глобальные ACID‑гарантии. Её цель — сохранить привычный Postgres и дать ему возможность расти горизонтально, без отказа от знакомых инструментов и контроля над данными.
fo_otman
Вроде делали ведь уже про это доклад на Highload++.