GH GambleHub

Шардинг і реплікація баз даних

Шардинг і реплікація баз даних

1) Навіщо це потрібно

Коли вертикальний апгрейд БД впирається в межі CPU/IO/RAM або один кластер стає SPOF, приходять реплікація (для читань/НА) і шардинг (для розподілу запису/даних). Цілі:
  • Пропускна здатність (горизонтальне зростання write QPS).
  • Доступність (швидкий failover, відсутність єдиної точки відмови).
  • Локалізація даних (мульти-регіон, низька латентність).
  • Ізоляція галасливих сусідів (hot tenants/hot keys).

2) Базові терміни та моделі узгодженості

Primary/Leader ↔ Replica/Follower: запис на лідері, читання - на репліках.
Синхронна реплікація: підтвердження транзакції після запису на N вузлах (низький RPO, вище латентність).
Асинхронна: лідер фіксує коміт і відправляє лог пізніше (RPO> 0, низька латентність).
Кворумна (Raft/Paxos): запис на більшість вузлів; один лог, автоматичний лідер.
Read-after-write: гарантоване читання своїх записів (див. § 5).

CAP в проді читаємо так: при мережевих проблемах ви вибираєте узгодженість (CP) або доступність (AP) для критичних операцій, часто комбінуючи рівні на різних шляхах.


3) Реплікація: варіанти та практики

3. 1 Фізична і логічна

Фізична (WAL/redo/binlog): ближче до блочного журналу, проста і швидка; обмежена гомогенною топологією/версією.
Логічна: потік DML/DDL на рівні рядків/таблиць; дозволяє часткові репліки, міграції між версіями, CDC для DWH/стрімінгу.

3. 2 Налаштування та управління

Контролюйте lag (час/байти/LSN).
Обмежте hot-standby feedback і довгі запити на репліках (щоб не стопорити VACUUM/клінінг).
Для MySQL - GTID і Orchestrator; для PostgreSQL — Patroni/replication slots, synchronous_standby_names.

PostgreSQL (синхронна репліка, фрагмент):
sql
-- на лидере
ALTER SYSTEM SET synchronous_commit = 'on';
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby_a, standby_b)';
SELECT pg_reload_conf();
MySQL GTID (ідентифікатор транзакції):
sql
SET GLOBAL enforce_gtid_consistency = ON;
SET GLOBAL gtid_mode = ON; -- перезапуск
CHANGE MASTER TO MASTER_AUTO_POSITION=1;

3. 3 Топології

1→N (лідер → репліки) + каскади (репліка зливає далі).
Multi-primary (active-active) - уникати в OLTP без суворого конфлікт-менеджменту.
Quorum-кластер (Raft) - CockroachDB/Yugabyte/PG-Raft-надбудови.


4) Read/Write Split і маршрутизація

Пишіть завжди в лідер; читайте з реплік, але враховуйте lag.

Стратегії read-after-write:

1. Session stickiness: після успішного запису клієнт читає з лідера протягом'Δ T'.

2. LSN/GTID-гейт: клієнт повідомляє «хочу не старіший LSN = X», роутер відправляє на репліку, чий LSN ≥ X.

3. Stale-ok: частина запитів допускає застарілі дані (каталоги/стрічки).

Інструменти: PgBouncer/Pgpool-II (Postgres), ProxySQL/MaxScale (MySQL), Vitess (маршрутизація шардів).

Приклад LSN-гейту (ідея): зберігайте'pg _ current _ wal _ lsn ()'в HTTP-хедер/куку і вимагайте від роутера репліку з'pg _ last _ wal _ replay _ lsn () ≥ LSN'.


5) Стратегії шардингу

5. 1 Вибір ключа

Ключ повинен забезпечувати рівномірність і локальність запитів:
  • Hash по'tenant _ id '/' user _ id'- рівномірно, але позбавляє range-сканів.
  • Range за часом/ID - відмінно для time-series/архіву, але ризик hot-shard.
  • Consistent hashing - спрощує додавання/видалення шардів.
  • Directory/lookup-таблиця - гнучко (будь-який алгоритм), але ще одна таблиця/кеш.

5. 2 Патерни

Shared-nothing: кожен шард - окрема БД/кластер, додаток знає маршрутизацію.
Middleware-шардинг: Vitess (MySQL), Citus (Postgres), Proxy-рівень ховає топологію.
Федерація: розділення доменів даних по сервісах (catalog, payments, auth).

5. 3 Композитні ключі

Використовуйте ключ-простір: `{tenant}:{entity}:{id}'і зберігайте це ж в додатку і кеші. Для Postgres — hash partitioning + LIST/RANGE subpartition.

PostgreSQL partitioning (фрагмент):
sql
CREATE TABLE orders (
tenant_id int,
id     bigint,
created_at timestamptz,
...,
PRIMARY KEY (tenant_id, id)
) PARTITION BY HASH (tenant_id);

CREATE TABLE orders_t0 PARTITION OF orders FOR VALUES WITH (MODULUS 16, REMAINDER 0);
--... t1..t15

6) Генерація ідентифікаторів

Уникайте «гарячих» монотонних автоінкрементів на шардінгу.
Використовуйте Snowflake-подібні 64-біт ID (time + region + shard + seq) або ULID/KSUID (монотонність і розподіленість).
Для Postgres — sequence per shard; для MySQL - auto_increment_increment/offset (різні офсети на лідерах шардів).


7) Онлайн-перешардування та міграції

Ключові принципи: подвійний запис (dual-write), ідемпотентність, тимчасова подвійна маршрутизація.

Кроки (узагальнено):

1. Додайте новий шард/кластер.

2. Увімкніть dual-read (перевірка консистентності).

3. Увімкніть dual-write (в обидва шарда), фіксуйте розбіжності.

4. Виконайте backfill історичних даних (батчі, реплікація логічна/CDC).

5. Перемкніть «джерело правди» на новий шард; залиште «хвостову» синхронізацію.

6. Вимкніть старий.

Інструменти: Vitess Resharding, Citus move shards, pg_logical/pgoutput, Debezium (CDC), gh-ost/pt-online-schema-change (DDL без блокировок).


8) Мульти-регіон і георозподіл

Leader-follower per region: локальні читання, запис - через глобальний лідер (проста модель, але cross-region RTT).
Multi-leader: запис в обох регіонах - потрібен конфлікт-мерджинг (timestamp/версія/CRDT).
True distributed SQL (Raft): CockroachDB/Yugabyte - дані «приклеєні» до регіону, запити йдуть в локальний кворум.

Рекомендації:
  • Гроші/замовлення - CP (кворум/лідер), каталоги/стрічки - AP (кеш, eventual).
  • Завжди плануйте write fencing (унікальні ключі/версіонування) при можливому split-brain.

9) Узгодженість на практиці

Read-your-writes: лідеру або репліці, «наздогнала» LSN/GTID.
Monotonic reads: «не старше» останнього прочитаного LSN.
Write-conflict control: `SELECT... FOR UPDATE', версії ('xmin '/' rowversion'), UPSERT з перевіркою версії.
Ідемпотентність: ключі ідемпотентності на оплатах/подіях.


10) Спостережуваність, SLO і алерти

Лаг реплік: час (сек), LSN distance (bytes), seconds_behind_master (MySQL).
Вимушені відкати/конфлікти, помилки реплікації.
p95/p99 latency по route (read leader vs replica, write).
Throughput: TPS/locks/row-contended tables.
Bloat/VACUUM (PG), InnoDB buffer pool hit ratio (MySQL).
Дашборди: per-shard навантаження, «гарячі» шарди, розподіл ключів.


11) Бекапи, PITR і DR

Повний бекап + WAL/binlog для PITR (point-in-time recovery).
Зберігайте в іншому регіоні/хмарі, регулярно робіть restore-тести.
Для шардів - узгоджений «зріз» (координація часу/LSN) або аплікативна ідемпотентність при відновленні.
RPO/RTO прописані і тестуються на game-days.

PostgreSQL base backup (ідея):
bash pg_basebackup -D /backups/base -X stream -C -S slot_replica_1 архивация WAL через archive_command или pgBackRest/Barman

12) Безпека та доступ

Сегментація по VPC/ACL, mTLS на проксі.
Ролі/гранти за принципом мінімальних прав; окремі користувачі на шард/роль.
Аудит DDL/DCL, ліміти на «важкі» запити на репліках.
Шифрування at rest (KMS) і в транзиті (TLS).
«Паніка-кнопка»: глобальний'READ ONLY'на час інциденту/розслідування.


13) Інструменти та цеглинки

PostgreSQL: Patroni (HA), PgBouncer (pooling/RO-routing), repmgr, pgBackRest/Barman (бэкап), Citus (шардинг), pglogical/Logical Replication, pgbadger/pg_stat_statements.
MySQL: Orchestrator (топології/авто-failover), ProxySQL/MaxScale (маршрутизація), Percona XtraBackup (бекап), Group Replication/InnoDB Cluster, Vuster itess (шардинг/ресхардинг).
Distributed SQL: CockroachDB, YugabyteDB (кворум, вбудований шардинг/геолокація).
CDC: Debezium + Kafka/Pulsar для подій/ETL.


14) Анти-патерни

Single-primary без авто-failover і без тестів DR.
«Магічний» read-split без урахування lag → фантомні помилки/підозрілі баги.
Шардінг «заради шардингу»: передчасне ускладнення замість вертикального скейла/індексів/кешу.
Гарячий діапазон (time-range) без time-bucket/hash-salt → один шард плавиться.
Глобальна транзакція 2PC поверх десятків шардів в OLTP - високі хвости p99 і часті блокування.
Відсутність dual-write/dual-read при міграціях → втрата/розсинхрон.
DDL в проді без онлайн-інструментів і без фіче-прапорів сумісності.


15) Чек-лист впровадження (0-60 днів)

0-15 днів

Визначити SLO БД, RPO/RTO.
Включити реплікацію, моніторинг lag, базові бекапи + PITR.
Ввести роутер (PgBouncer/ProxySQL) і політику read-after-write.

16-30 днів

Вибрати стратегію шардингу, описати ключі і схеми.
Підготувати інструменти перешардування (Vitess/Citus/CDC).
Каталог сервісів/таблиць з позначкою «read-stale-ok» vs «strict».

31-60 днів

Запустити pilot-шард, dual-read і backfill.
Game-day: failover лідера, відновлення з PITR, перемикання регіону.
Автоматизувати звіти по гарячих шард-ключах і нерівномірності.


16) Метрики зрілості

Replica lag p95 <цільового (наприклад, 500 ms) для критичних читань.
Успішні DR-тести ≥ 1/квартал (restore ≤ RTO, втрата ≤ RPO).
Розподіл навантаження по шардах: дисбаланс <20% по QPS/сховищу.
Частка запитів зі strict-consistency, коректно маршрутизована, = 100%.
Zero-data-loss в інцидентах, що вимагають CP-гарантій (гроші/замовлення).
Онлайн DDL/міграції без простоїв, з прапорами сумісності.


17) Приклади рецептів

Hash-salt для time-range (щоб не гріти один шард):
sql
-- вычисляйте bucket = hash(user_id) % 16, храните (bucket, created_at)
PARTITION BY LIST (bucket) SUBPARTITION BY RANGE (created_at)
Read-my-writes middleware (псевдокод):
python lsn = db.leader_query("SELECT pg_current_wal_lsn()")
ctx.sticky_until = now()+5s ctx.min_lsn = lsn в роутере чтений: выбираем реплику с last_lsn >= ctx.min_lsn, иначе лидер
Vitess VSchema (фрагмент):
json
{
"tables": {
"orders": { "column_vindexes": [{ "column": "tenant_id", "name": "hash" }] }
}
}

18) Висновок

Шардинг і реплікація - це не тільки техніка, але і процеси: маршрутизація з урахуванням узгодженості, дисципліна міграцій (dual-write/read, backfill), регулярні DR-тести і спостережуваність lag/гарячих шардів. Починайте з простого leader→replica + read-after-write, потім додавайте шардинг там, де його дійсно вимагає профіль навантаження. Використовуйте готові платформи (Vitess/Citus/Distributed SQL) і тримайте бізнес-критичні дані в CP-режимі - так база перестане бути пляшковим горлечком і стане передбачуваним, еластичним фундаментом платформи.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.