
В современных реалиях объёмы данных постоянно растут, и появляются всё более жёсткие требования к производительности. Тут традиционный 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 и дать ему возможность расти горизонтально, без отказа от знакомых инструментов и контроля над данными.
Автор: denchickkk
