awesome-everything EN
↑ Обратно к восхождению

Базы данных

Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффов

Суть Шардинг — последнее средство, выбираемое когда пределы одного Postgres измерены и доказаны. Shard key необратим, co-location определяет какие join-ы остаются локальными, а каскад трейдоффов показывает почему пропуск любого раннего акта делает шардинг дороже.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 14 min

Citus развёрнут. Shard key — tenant_id. Большинство tenants быстры. Tenant Acme — 40% всех запросов — насыщает свой шард, пока пять других простаивают. Кластер фактически одношардовый для Acme. Сбой hot shard был не вызван шардингом; он был спроектирован в день выбора shard key.

Акт 7 — Три независимых решения шардинга

Шардинг в год 3 включает три независимых решения:

Решение 1: Shard key. Shard key определяет распределение данных и локальность запросов. Для B2B multi-tenant SaaS tenant_id — естественный выбор: все запросы для одного tenant остаются на одном шарде. Для B2C — user_id. Для time-series — time bucket. Ключ должен иметь высокую кардинальность (для равномерного распределения нагрузки), равномерные паттерны доступа (чтобы ни один шард не был горячим) и быть дешёвым в вычислении (чтобы routing overhead был минимальным).

Решение 2: Метод распределения.

  • Hash: строки хешируются на шарды — равномерное распределение, range queries веером расходятся по всем шардам.
  • Range: строки в диапазоне ключей идут на конкретный шард — range queries остаются локальными, но горячий диапазон создаёт горячий шард.
  • List: ручное назначение — гибко, высокие операционные издержки.
  • Directory: lookup table маппит значения ключей на шарды — максимальная гибкость, максимальные операционные издержки.

Решение 3: Co-location. Таблицы, участвующие в join-ах, должны быть размещены на одном shard key, чтобы cross-shard join-ы оставались локальными. В Citus: users, orgs, events, audit_log — все шардированные по tenant_id — co-located. Join между users и events для tenant 42 попадает на один шард и возвращается с локальной латентностью. Без co-location тот же join веером идёт по всем шардам, собирает результаты на coordinator и мёрджит.

Federation tax

После шардирования запросы, охватывающие несколько шардов, должны быть federated: отправить запрос на каждый шард, собрать результаты и смёрджить на стороне приложения или на Citus coordinator.

Наивный outer join через два шарда становится двумя inner queries (по одному на шард) и client-side join. GROUP BY, затрагивающий шарды, становится GROUP BY на каждом шарде, затем второй GROUP BY по агрегатам. Каждый cross-shard запрос имеет latency = max(latency по шарду) + merge time; хвостовая латентность доминирует.

Citus обрабатывает federation с distributed query planning, но только для co-located таблиц (с одинаковым shard key). Reference tables (реплицированные на каждый worker-узел) бесплатны — маленькая dimension table типа countries или currencies может быть реплицирована один раз и join-иться локально везде.

Исправление hot shard

Когда Acme даёт 40% всех запросов на tenant_id-шардированном кластере:

  • Вариант A — логическое разбиение: Направить запросы Acme на выделенный набор логических шардов. Tenant-aware router мультиплексирует Acme по shard ID, маппящимся на выделенный физический worker. Остальные tenants делят оставшиеся workers.
  • Вариант B — физический кластер: Переместить Acme на свой собственный Postgres кластер. Запросы к Acme маршрутизируются туда; общий кластер обрабатывает все остальные tenants.

Оба варианта требуют онлайн-решардинга (citus_rebalance_table_shards), использующего логическую репликацию для суб-секундных пауз записи на шард. Планируй окно — догоняющая репликация добавляет нагрузку.

Семиуровневый каскад трейдоффов

Каждый акт открывает следующий, но только если предыдущий в порядке:

  • Пропусти Акт 1 (схема) → Акты 2–7 воюют с неэффективными join-ами и изменяемыми натуральными ключами.
  • Пропусти Акт 2 (индексы) → решения планирования Акта 3 принимаются вслепую; статистика бесполезна, если планировщику нечего выбирать.
  • Пропусти Акт 3 (статистика) → vacuum Акта 4 единственный рычаг; таблицы раздуваются пока ты угадываешь мощность.
  • Пропусти Акт 4 (bloat) → потоки пула Акта 5 видят table scans, замедленные мёртвыми tuples в heap pages.
  • Пропусти Акт 5 (размер пула) → миграции Акта 6 задыхаются от connection storms; backlog handshakes соединений мешает миграции получить блокировки вовремя.
  • Пропусти Акт 6 (lock safety) → шардинг Акта 7 невозможен без downtime; каждый shard rebalance требует эксклюзивной блокировки если миграции не были разработаны для онлайн-работы.
  • Пропусти Акт 7 (когда нужен) → один tenant доминирует в кластере, и остальные ждут в очереди.

Порядок — ограничение, наложенное физикой и внутренностями Postgres, а не рекомендация.

Акт 7: референсные числа шардинга
Single-node ceiling, write-heavy OLTP (железо 2026)
10–50K writes/с sustained
Полный скан таблицы 1B строк на SSD
5–10 мин
Онлайн-перемещение шарда, пауза записи (Citus 11.1+)
менее 1 секунды на шард
Schema-based sharding: практический лимит tenants на кластер
1000–3000 tenants
Проект изменения shard key
месяцы (dual-write + backfill + cutover)
Почему это работает

Schema-based multi-tenancy («одна схема на tenant») хорошо работает при малом числе tenants: чистая изоляция, простое резервное копирование, простой экспорт данных. Но catalog-таблицы Postgres (pg_class, pg_attribute, pg_constraint) растут линейно с числом схем × таблиц на схему. При 10000 схем × 50 таблиц в pg_class полмиллиона строк; время обхода планировщика в запросах, затрагивающих несколько схем, вырастает до секунд. Практический потолок — 1000–3000 tenants на кластер при schema-based sharding. После этого row-level multi-tenancy (колонка tenant_id в каждой таблице) с Citus shard distribution масштабируется дальше.

Викторина

Кластер Citus шардирован по tenant_id. Запрос join-ит users и events для одного tenant. Заплатит ли этот запрос federation tax?

Викторина

Команда пропустила Акт 6 (паттерны безопасных миграций). Теперь нужно добавить колонку в шардированную таблицу на 1B строк. Каково последствие?

Викторина

Почему изменение shard key после запуска описывается как 'одна из самых дорогих операций в базах данных'?

Вспомните перед уходом
  1. 01
    Назови три независимых решения шардинга и объясни почему shard key наиболее значим.
  2. 02
    Что такое federation tax и когда co-location его устраняет?
  3. 03
    Проследи каскад трейдоффов: выбери Акты 1 и 6, опиши как их пропуск делает Акт 7 дороже.
Итог

Акт 7 включает три решения: shard key (в основном необратимый — определяет распределение данных и локальность join-ов), метод распределения (hash для равномерного распределения, range для локальности range-запросов, list/directory для ручного управления) и co-location (таблицы, join-ящиеся вместе, должны разделять shard key, чтобы избежать federation tax). Failure mode hot shard — один tenant насыщает шард — исправляется логическим разбиением tenant или физической изоляцией кластера, оба через онлайн-решардинг. Каскад трейдоффов доказывает: акты компонуются — пропусти Акт 1 и выбор shard key отравлен неверной схемой; пропусти Акт 6 и каждая операция шарда — downtime event. Прагматичный дефолт 2026 для Postgres-шопа, масштабирующегося за один узел: declarative partitioning сначала, Citus когда пределы одного узла измерены и доказаны, Aurora DSQL или Spanner только когда глобальные writes — реальное product requirement.

Связанные уроки
встречается в263
Продолжить восхождение ↑Наблюдаемость, антипаттерны и производственный триаж
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.