PostgreSQL
Citus: горизонтальное шардирование PostgreSQL
Notion в 2021: 100+ миллионов страниц, монолитный PostgreSQL. Latency растёт, вертикальное масштабирование упирается в потолок. Решение: Citus. Вместо переписывания на Cassandra или DynamoDB (месяцы разработки) - добавить extension PostgreSQL. Шардирование по workspace_id. Те же SQL запросы, тот же ORM, тот же код. 10x рост данных без деградации. Citus - это шардирование без смены стека.
- **Notion** - Citus для шардирования по workspace_id: 100M+ страниц, горизонтальное масштабирование без смены application code
- **Microsoft Azure** - Citus под капотом Azure Database for PostgreSQL Hyperscale: autoscaling, automatic rebalancing, managed HA
- **Algolia** - Citus для analytics: 10B+ событий/день, real-time aggregations через distributed queries, без ETL в отдельную OLAP систему
Citus: PostgreSQL as a distributed database
**Citus** - PostgreSQL extension, превращающий кластер Postgres-серверов в распределённую БД. Coordinator node принимает запросы, routing их к worker nodes. Шардирование прозрачно для приложения: обычный PostgreSQL connection string, обычный SQL. Microsoft приобрела Citus Data в 2019, Citus стал частью Azure Database for PostgreSQL.
Citus coordinator получил запрос `SELECT COUNT(*) FROM orders WHERE status = 'completed'`. Как он его обработает?
Distributed Tables: шардирование данных
**Distributed table** - таблица, данные которой распределены по shards на worker nodes. Выбор **distribution column** (шард-ключа) - критически важное решение. Все строки с одинаковым значением distribution column хранятся на одном shard. Это определяет эффективность JOIN и локальность транзакций.
**Distribution column нельзя изменить** после создания distributed table - нужно пересоздать таблицу. Выбирайте тщательно: tenant_id для multi-tenant SaaS, user_id для user-centric данных, region для geo-partitioned данных.
Таблица orders распределена по tenant_id. Запрос: `SELECT * FROM orders WHERE id = 12345` (без tenant_id в WHERE). Что делает coordinator?
Colocation: совместное размещение связанных данных
**Colocation** - размещение связанных строк из разных таблиц на одном shard. Если `orders` и `order_items` colocated по `tenant_id`, то JOIN для одного tenant выполняется локально на одном worker без сети. Это ключ к производительности distributed JOIN.
**Citus colocation** реализует архитектурный принцип 'data that is queried together should live together'. Правильная colocation strategy позволяет 90%+ JOIN выполнять локально. Для multi-tenant SaaS: все таблицы по tenant_id = все запросы одного клиента на одном worker.
orders и products не colocated (orders по tenant_id, products по category_id). JOIN orders JOIN products по product_id. Что произойдёт?
Distributed Queries: возможности и ограничения
**Citus поддерживает большинство PostgreSQL запросов** в distributed режиме. Ограничения связаны с cross-shard транзакциями и некоторыми SQL конструкциями. Знание ограничений критично для проектирования схемы.
**Citus routing modes:** когда distribution column в WHERE - router execution (один worker). Без него - real-time executor (все workers параллельно). Проверить режим: EXPLAIN запроса покажет 'Custom Scan (Citus Router)' или 'Custom Scan (Citus Adaptive)'.
Таблица users распределена по user_id. Нужна уникальность email. Как добиться?
Rebalancing: перераспределение данных
**Rebalancing** - перераспределение shards между workers при добавлении нового узла или неравномерной нагрузке. Citus поддерживает online rebalancing (без downtime): shards перемещаются по одному, данные копируются, трафик переключается автоматически.
**Hyperscale (Citus) на Azure** - managed Citus: autoscaling workers, automatic rebalancing при добавлении compute. Microsoft использует это для Azure Cosmos DB for PostgreSQL. Notion перешёл с MongoDB на Citus-based PostgreSQL для масштабирования.
Citus - полная замена PostgreSQL, требует переписи запросов
Citus - расширение PostgreSQL, не замена. Большинство запросов работает без изменений. Ограничения возникают при cross-shard уникальности и некоторых JOIN паттернах
Citus использует стандартный PostgreSQL wire protocol. Приложение подключается как к обычному PostgreSQL. Coordinator преобразует запросы в distributed план прозрачно. Нужны изменения только при выборе правильного distribution column и colocation strategy - архитектурные решения, не переписывание запросов
Citus кластер: 3 workers, 32 shards. Добавлен 4-й worker. Rebalance завершён. Сколько shards на каждом worker?
Итоги
- **Citus = PostgreSQL extension** для горизонтального шардирования. Coordinator + workers, стандартный PostgreSQL API
- **Distribution column** - ключевое архитектурное решение. Нельзя изменить без пересоздания. tenant_id, user_id - типичные выборы
- **Colocation** - размещать связанные таблицы на одних shards. JOIN colocated данных = локально, без сети
- **Reference tables** - полная копия на каждом worker. Для справочников и таблиц которые JOIN'ятся с distributed
- **Rebalancing** онлайн: добавить worker → rebalance_table_shards() → равномерное распределение без downtime
Связанные темы
Citus строится поверх PostgreSQL и дополняет другие механизмы масштабирования:
- PostgreSQL на масштабе — Citus - один из паттернов масштабирования. Другие: read replicas, partitioning, external caching
- Партиционирование — Citus distributed tables vs PostgreSQL native partitioning: разные уровни (cross-server vs single-server)
- Streaming Replication — Каждый Citus worker имеет свои standby реплики через streaming replication для HA
Вопросы для размышления
- Multi-tenant SaaS: 10K клиентов, таблица `events` с 1B строк. Распределить по tenant_id или event_type? Как это решение влияет на эффективность запроса `SELECT * FROM events WHERE tenant_id = 42 AND event_type = 'purchase'`?
- Citus кластер: orders (распределена по tenant_id) и products (distributed по category_id). Запрос `SELECT * FROM orders o JOIN products p ON o.product_id = p.id WHERE tenant_id = 42`. Что будет происходить? Как переделать схему для эффективного JOIN?
- Добавлен 5-й worker в Citus кластер с 4 workers и 32 shards. Rebalance начался. Трафик идёт. Что происходит с запросами к shards которые в процессе перемещения?