Перейти к содержимому

28. Distributed SQL, ClickHouse, Tarantool

Зачем знать на Middle 3: Senior выбирает БД под задачу. Когда нужен geo-distributed strong consistency — это CockroachDB / YugabyteDB / YDB / TiDB. Когда нужны OLAP-аналитика поверх миллиардов строк — ClickHouse. Когда нужен микросекундный latency и in-memory store — Tarantool. Каждая из них радикально отличается по архитектуре и моделям данных. Без понимания их свойств выбор сводится к «модно/слышали» — а это путь к 6-значным потерям.


  1. Концепция
  2. Production-deep dive
  3. Gotchas
  4. Real cases
  5. Вопросы
  6. Practice
  7. Источники

┌─────────────────────────────────────────────────────────────┐
│ OLTP (row-store, ACID, точечные апдейты) │
│ PostgreSQL ─── single-node / streaming repl │
│ CockroachDB ──┐ │
│ YugabyteDB ├── distributed SQL (NewSQL) │
│ YDB │ strong consistency, multi-region │
│ TiDB ┘ │
│ Tarantool ─── in-memory, microsecond latency │
├─────────────────────────────────────────────────────────────┤
│ OLAP (column-store, batch, aggregations) │
│ ClickHouse ─── распределённый, real-time │
│ DuckDB ─── embedded analytics │
└─────────────────────────────────────────────────────────────┘

NewSQL / Distributed SQL — это попытка совместить:

  • ACID-транзакции (как Postgres),
  • горизонтальное масштабирование (как Cassandra),
  • SQL-интерфейс,
  • strong consistency между регионами.

Под капотом обычно — Raft-based consensus + MVCC + распределённый storage (KV-layer типа RocksDB/Pebble).

OLAP column-store — данные хранятся по колонкам. Это даёт колоссальное сжатие (одна колонка = однородные значения, sort + RLE), быстрое чтение нужных колонок (нет лишнего IO) и быструю агрегацию (SIMD). Но точечные апдейты — невыполнимы или медленные.

In-memory DB (Tarantool, Redis, Aerospike) — данные в RAM, диск только для durability (WAL + snapshot). Latency микросекунды, throughput миллионы RPS.


CockroachDB — Postgres-wire-compatible distributed SQL, написана на Go.

Архитектура:

┌────────────────────────────────────────────────┐
│ CockroachDB cluster (Go) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ node 1 │ │ node 2 │ │ node 3 │ │
│ │ ┌─────┐ │ │ ┌─────┐ │ │ ┌─────┐ │ │
│ │ │SQL │ │ │ │SQL │ │ │ │SQL │ │ │
│ │ └──┬──┘ │ │ └──┬──┘ │ │ └──┬──┘ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ ┌──┴──┐ │ │ ┌──┴──┐ │ │ ┌──┴──┐ │ │
│ │ │ KV │ │ │ │ KV │ │ │ │ KV │ │ │
│ │ │Raft │◄┼───┼─►│Raft │◄┼───┼─►│Raft │ │ │
│ │ └──┬──┘ │ │ └──┬──┘ │ │ └──┬──┘ │ │
│ │ ┌──┴──┐ │ │ ┌──┴──┐ │ │ ┌──┴──┐ │ │
│ │ │Pebble│ │ │ │Pebble│ │ │ │Pebble│ │ │
│ │ └─────┘ │ │ └─────┘ │ │ └─────┘ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────────────────────────────┘
  • Данные разбиты на ranges (~512 MiB по умолчанию).
  • Каждый range реплицирован Raft group на 3+ узла.
  • Lease holder обрабатывает чтения (через follower-reads — можно с follower’ов).
  • Multi-version timestamps (HLC — Hybrid Logical Clock).
  • Wire-protocol Postgres → drop-in replacement для большинства Go-pgx/lib-pq приложений.

Use cases:

  • Финтех с глобальной аудиторией (multi-region writes).
  • Сильно растущие SaaS с непредсказуемым шардингом.
  • Замена Postgres, когда нужна горизонталь.

Go-driver: обычный pgx подходит. Особенности:

  • Retryable errors (40001 — serialization conflict) — нужно retry в приложении.
  • Transactions могут идти долго — будь готов к долгим коммитам.

Локализация (multi-region):

ALTER DATABASE app PRIMARY REGION "eu-central-1";
ALTER DATABASE app ADD REGION "us-east-1";
ALTER TABLE users SET LOCALITY REGIONAL BY ROW AS region;

Каждая строка живёт в своём регионе → reads/writes локальные.

PG-compatible, distributed.

Архитектура: YB-TServer (data) + YB-Master (metadata). KV layer DocDB поверх RocksDB. Inspired by Google Spanner.

Layer’ы:

  • YSQL — Postgres-совместимый SQL.
  • YCQL — Cassandra-compatible (для key-value).

Особенности:

  • Замечательная PG-совместимость (форк PG-кода).
  • Transactional + распределённый.
  • Tablet-based sharding (auto-split при росте).
  • Колоночные индексы, materialized views.

Drawback: меньше зрелости, чем CRDB; конфликты с PG-расширениями.

Распределённая SQL от Яндекс. Production scale (Я.Метрика, Я.Маркет, Я.Облако).

Свойства:

  • Strong serializable transactions (snapshot isolation + read-write).
  • Multi-DC, geo-replication.
  • Распределённое хранилище (BlobStorage, like GFS).
  • Coordinators + Datashards (как Spanner: TrueTime → у YDB свой clock).
  • Внутренний механизм 2-phase commit между шардами.

API:

  • SQL (YQL — YDB Query Language, super-set SQL).
  • gRPC API.
  • Topics (Kafka-like) — встроенная очередь.
  • Coordination service (etcd-like).

Go SDK:

import "github.com/ydb-platform/ydb-go-sdk/v3"
db, _ := ydb.Open(ctx, "grpcs://ydb.demo:2135/?database=/local")
defer db.Close(ctx)
err := db.Table().Do(ctx, func(ctx context.Context, s table.Session) error {
res, err := s.Execute(ctx, table.SerializableReadWriteTxControl(table.CommitTx()),
`SELECT id, name FROM users WHERE id = $id;`,
table.NewQueryParameters(table.ValueParam("$id", types.Uint64Value(1))))
if err != nil { return err }
defer res.Close()
// ...
return nil
})

Особенности YDB Go SDK:

  • Сессия — first-class citizen.
  • Retry хелперы (idempotent retry на TLI и сетевых ошибках).
  • Подготовленные параметры — типизированные.
  • Топики — Kafka-like, но с собственным протоколом.

Когда YDB:

  • В Yandex Cloud (managed).
  • Workload, где Postgres не масштабируется, но нужна SQL и strong consistency.
  • Замена связки Kafka + Postgres (топики + tables в одной системе).

MySQL-wire-compatible distributed SQL (PingCAP, China).

Архитектура:

  • TiDB (SQL layer, stateless).
  • TiKV (Raft-based KV layer, Rust).
  • PD (Placement Driver, scheduler).
  • TiFlash (column-store для OLAP query — HTAP).

Особенности:

  • HTAP: row + column copies синхронизируются.
  • MySQL compatibility — почти drop-in.
  • Полностью open-source.

Use case: замена шардированного MySQL.

ClickHouse — column-store от Яндекс / ClickHouse Inc. Сейчас флагман для real-time OLAP, observability, реклама, BI.

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ CH node 1 │ │ CH node 2 │ │ CH node 3 │
│ │ │ │ │ │
│ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ Replica A │◄──►│ Replica A │◄──►│ Replica A │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└──────────────────┼──────────────────┘
┌──────────────────────┐
│ Distributed table │ (router)
└──────────────────────┘
┌──────────────────────┐
│ ClickHouse Keeper / │
│ ZooKeeper │ (replication coord)
└──────────────────────┘

Это сердце ClickHouse. Все Engine’ы:

EngineНазначение
MergeTreeБазовый: sorted, partitioned
ReplacingMergeTreeДедупликация по ORDER BY ключу при мерже
SummingMergeTreeАвто-суммирование числовых колонок при мерже
AggregatingMergeTreeХранит AggregateFunction state, мерж суммирует
CollapsingMergeTreeУдаление пар (Sign +1 / -1) для CDC-like
VersionedCollapsingMTТо же, но с версией
GraphiteMergeTreeДля метрик (rollup-aware)

Replicated*MergeTree — добавляет ZK/Keeper координацию.

Создание таблицы:

CREATE TABLE events
(
event_date Date,
event_time DateTime,
user_id UInt64,
event_type LowCardinality(String),
payload String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')
PARTITION BY toYYYYMM(event_date)
ORDER BY (user_id, event_time)
SETTINGS index_granularity = 8192;

Ключевые понятия:

  • Partition — физическое разделение (директории). Партиция = единица DROP / TTL.
  • ORDER BY = primary key (хранится sparse index с шагом index_granularity, обычно 8192 строки). Это главный механизм быстрого чтения.
  • Index granularity — каждые N строк один отметчик в primary index. Меньше = больше точность, больше overhead.
  • Background merges — мелкие parts сливаются в большие фоновым process’ом. До мержа дубликаты не схлопываются (важно для ReplacingMergeTree).

⚠️ Главное правило ClickHouse: вставляй пачками 1000-100000 строк. Каждый INSERT = одна part = одна директория. Тысячи мелких INSERT’ов → миллион partов → background merge не успевает → деградация.

Batch from Go:

import (
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
)
conn, err := clickhouse.Open(&clickhouse.Options{
Addr: []string{"clickhouse:9000"},
Auth: clickhouse.Auth{Database: "app", Username: "default", Password: "..."},
Settings: clickhouse.Settings{"async_insert": 1, "wait_for_async_insert": 0},
})
batch, _ := conn.PrepareBatch(ctx, "INSERT INTO events (event_date, event_time, user_id, event_type, payload)")
for _, e := range events {
_ = batch.Append(e.Date, e.Time, e.UserID, e.Type, e.Payload)
}
_ = batch.Send()

async_insert (CH 21.11+) — сервер сам буферизует мелкие inserts. Меняет правила игры для апп-серверов, которые не могут батчить.

В CH materialized view — это trigger на INSERT. При вставке в источник, MV сам считает агрегат и сохраняет в свою таблицу.

CREATE MATERIALIZED VIEW events_hourly
ENGINE = SummingMergeTree
PARTITION BY toYYYYMM(hour) ORDER BY (hour, user_id)
AS
SELECT toStartOfHour(event_time) AS hour, user_id, count() AS cnt
FROM events
GROUP BY hour, user_id;

Опасности:

  • Не reads источник, а только новые INSERT’ы.
  • Если переименовал/дропнул source — MV сломается.
  • Initial load — POPULATE (нагрузка, не атомарно), либо ручной BACKFILL.
ALTER TABLE events ADD INDEX idx_user user_id TYPE minmax GRANULARITY 4;
ALTER TABLE events ADD INDEX idx_type event_type TYPE set(100) GRANULARITY 4;
ALTER TABLE events ADD INDEX idx_text payload TYPE bloom_filter(0.01) GRANULARITY 1;

Это skip indexes — они не указывают «где есть», а позволяют пропускать блоки.

CH сначала читает условие WHERE по колонкам, потом дочитывает остальные. PREWHERE явно указывает, какое условие пускать первым (читать самую дешёвую колонку):

SELECT user_id, payload FROM events
PREWHERE event_type = 'click'
WHERE user_id > 1000;

CH чаще сам ставит PREWHERE (optimize_move_to_prewhere = 1), но для сложных кейсов — руками.

CREATE TABLE events_distributed AS events
ENGINE = Distributed(my_cluster, app, events, rand());
  • Routing INSERT’ов по shards (по hash, rand, or expression).
  • Параллельные SELECT’ы на все shards с агрегацией результата.
DriverЗамечания
ClickHouse/clickhouse-go v2Официальный, native protocol, async insert, generics
mailru/go-clickhouseHTTP-protocol, проще, медленнее
uptrace/go-clickhouseORM-like, generators

ClickHouse — главный backend для логов/метрик/трейсов (OpenTelemetry, Grafana, Signoz, Uptrace).

Почему не Kafka / PG / ES:

  • Сжатие 10-30x на структурированных логах (LZ4/ZSTD + LowCardinality columns).
  • Быстрая агрегация: count(), avg(), quantile() за миллисекунды на миллиардах строк.
  • Real-time: insert latency 1-2 секунды (background merge).
  • Дешёвый storage: данные могут лежать на S3 (через S3 disk).

Tarantool — in-memory DB + application server (на Lua, расширения C/Go) от Mail.ru / VK.

Свойства:

  • Данные в RAM, persistent на диск (snapshot + WAL).
  • Latency ~10-100 микросекунд.
  • Throughput 100k-1M ops/sec на одну ноду.
  • Repl. — async или sync (Raft с Tarantool 2.10+).
  • Sharding — Vshard (отдельная библиотека).

Модель данных:

  • Space — таблица.
  • Tuple — строка (массив значений).
  • Index — TREE, HASH, BITSET, RTREE.
  • Schema-less или schemafull (format).
box.cfg{ listen = 3301, wal_mode = 'write' }
box.schema.space.create('users', { if_not_exists = true })
box.space.users:format({
{name='id', type='unsigned'},
{name='name', type='string'},
{name='age', type='unsigned'},
})
box.space.users:create_index('primary', { parts = {'id'} })
box.space.users:create_index('by_name', { parts = {'name'}, unique = false })
box.space.users:insert{1, 'Alice', 30}

Go client (tarantool/go-tarantool):

import "github.com/tarantool/go-tarantool/v2"
conn, _ := tarantool.Connect(ctx, dialer, tarantool.Opts{})
defer conn.Close()
resp, _ := conn.Do(tarantool.NewSelectRequest("users").
Index("primary").Iterator(tarantool.IterEq).
Key([]interface{}{uint64(1)})).Get()

Use cases:

  • Hot cache + WAL (вместо Redis + Postgres).
  • Сессии (миллионы конкурентных пользователей).
  • Anti-fraud правила (микросекундный response).
  • Featurestores (real-time ML features).

Where: VK, Mail.ru, Alfa-Bank, Sberbank, Russian Post, X5.

⚠️ Tarantool — RAM-based. Объём данных ограничен памятью сервера. Для холодных данных — комбинируй с другой БД.


CRDB по умолчанию SERIALIZABLE. Конкурентные транзакции часто падают 40001. Приложение обязано retry.

for retries := 0; retries < 3; retries++ {
err := doTx(ctx, db)
if err == nil { break }
if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == "40001" {
continue
}
return err
}

NewSQL хорош для multi-region. Если у тебя один дата-центр — overhead Raft’а делает их медленнее одной Postgres. Не выбирай NewSQL, если distribution не нужна.

В YDB параметры запроса нужно объявлять явно с типами. Неправильный тип = runtime ошибка. Используй типизированные параметры из SDK.

Тысячи мелких INSERT’ов = катастрофа. Используй async_insert или батчинг.

ALTER TABLE … UPDATE ... WHERE — это mutation, выполняется фоновой задачей, переписывает партиции. На больших таблицах — часы. Для real-time изменений — ReplacingMergeTree (последнее значение по ключу).

Нельзя добавить второй «индекс по другому ключу» как в Postgres. Можно либо проекции (projection), либо вторичная агрегатная таблица через MV.

LowCardinality(String) для колонок с малым числом уникальных значений (status, country) — сжатие 10-100x, фильтрация быстрее.

Очень большие batch (миллион строк) могут упасть с OOM на сервере. Контролируй через max_block_size и max_insert_block_size.

INSERT в Distributed таблицу — асинхронный по shards (по умолчанию). Без insert_distributed_sync=1 может потеряться при перезагрузке.

DROP source таблицы — MV ломается. ATTACH/DETACH source — данные не доходят. Используй EXCHANGE TABLES для атомарной замены.

Если упал, при старте redo WAL может занять минуты. Регулярно делай checkpoint (box.snapshot()).

Сервер падает, если данные не влезают в RAM (или в memtx_memory). Мониторь box.slab.info().

Lua-функции, выделяющие много объектов, могут вызвать GC паузу — это блокирует event loop. Пиши hot path на C или используй box.tuple.

Vshard поддерживает онлайн-перебалансировку, но при больших объёмах — нагрузка сравнима с initial setup. Планируй заранее.

CRDB / YugabyteDB ≈ Postgres, но не на 100%. Расширения (pg_trgm, postgis) могут не работать. ORM (GORM, Ent) обычно ок, но тесты обязательны.


Контекст: MySQL шардированный (8 шардов руками), 20TB, нагрузка растёт.

Решение: TiDB cluster (3 TiDB, 6 TiKV, 3 PD). Миграция через TiDB Data Migration tool. Минусы: latency p99 вырос с 5ms до 15ms (Raft overhead). Плюсы: SQL JOINs между бывшими шардами; авто-rebalance; analytic queries через TiFlash.

Контекст: 200 микросервисов, 50k req/s, нужно хранить логи 30 дней.

Stack:

  • OpenTelemetry collector → Kafka (буфер) → ClickHouse (через ClickHouse Sink).
  • Schema: ts DateTime64, service LowCardinality(String), level LowCardinality(String), trace_id String, message String, attrs Map(String, String).
  • TTL: ttl_date + INTERVAL 30 DAY DELETE.
  • Cold storage: S3 disk через tiered_storage.

Результат: 5TB/day сжимается до 250GB/day. Запросы по trace_id — < 100ms.

Контекст: Web-app, 30M активных сессий, 500k RPS на чтение, нужна latency p99 < 1ms.

Решение: Tarantool 1.10 (memtx + WAL), 3 ноды, vshard. Каждая нода 64GB RAM, sessions ~20GB total. WAL на NVMe. Latency p99 0.4ms. Стоит в 5 раз дешевле, чем тот же объём в RDS.

Контекст: Бирж-приложение, multi-region (EU + US), требование zero data loss + strong consistency для балансов.

Решение:

  • CRDB cluster 9 нод (3 регионa × 3 ноды).
  • Таблица balances локализована по region (REGIONAL BY ROW).
  • Финансовые транзакции — SERIALIZABLE, ретраи в приложении.

Узкое место: cross-region transfer латентность ~80ms (2 phase commit). Решили оптимистичным двух-шаговым переводом (commit local → enqueue async transfer).

Контекст: Yandex Cloud, multi-region SaaS, нужен ACID + 99.99% uptime.

Решение: YDB managed, схема — обычный реляционный design + топики для очередей событий. PostgreSQL — для аналитики (отдельно). Latency коммита транзакции 5-15ms, throughput 50k TPS.


  1. Что такое NewSQL и чем он отличается от классических распределённых БД?
  2. Как CockroachDB обеспечивает strong consistency? Что такое HLC?
  3. Что такое range / tablet в распределённых SQL? Как они делятся?
  4. Почему CRDB в одном дата-центре медленнее Postgres?
  5. Что такое REGIONAL BY ROW в CRDB и зачем?
  6. Какие гарантии транзакций даёт YDB? Что такое YQL?
  7. Чем YDB topics отличаются от Kafka?
  8. Что такое TiDB HTAP и зачем TiFlash?
  9. В чём суть column-store? Почему он быстрый для аналитики?
  10. Какие MergeTree engines есть в ClickHouse и для чего каждый?
  11. Что такое ORDER BY в CH и почему это «первичный ключ»?
  12. Что такое index_granularity и granule?
  13. Почему ClickHouse не любит мелкие INSERT’ы? Что такое async_insert?
  14. Что такое Materialized View в CH? Чем отличается от PG?
  15. Что такое PREWHERE и когда оно полезно?
  16. Что такое skip indexes (minmax, set, bloom_filter)?
  17. Что такое Distributed engine? Как идут SELECT и INSERT?
  18. Что такое LowCardinality(...) и зачем?
  19. Как ClickHouse реплицируется? Что такое Keeper?
  20. Почему UPDATE в CH — дорогая операция?
  21. Что такое Tarantool и для каких задач выбирают?
  22. Что такое space, tuple, index в Tarantool?
  23. Чем memtx отличается от vinyl?
  24. Что такое Vshard?
  25. Когда выбрать ClickHouse vs Postgres для логов?

Окно терминала
docker run -d --name=crdb -p 26257:26257 -p 8080:8080 \
cockroachdb/cockroach:latest start-single-node --insecure
psql "postgres://root@localhost:26257/defaultdb?sslmode=disable"

Создай таблицу, запусти EXPLAIN (CRDB-специфичный), посмотри на KV/Locking info.

Окно терминала
docker run -d --name ch -p 9000:9000 -p 8123:8123 clickhouse/clickhouse-server
CREATE TABLE events (ts DateTime, user_id UInt64, payload String) ENGINE = MergeTree ORDER BY (user_id, ts);

Из Go вставляй батчами 10k строк, замерь throughput.

CREATE MATERIALIZED VIEW events_per_user
ENGINE = SummingMergeTree ORDER BY user_id
AS SELECT user_id, count() AS cnt FROM events GROUP BY user_id;

Вставь 1M строк, проверь, что MV видит.

Окно терминала
docker run -p 3301:3301 -d --name tnt tarantool/tarantool:2.11

Подключись через tarantoolctl connect ..., создай space, индекс, вставь тапл.

Окно терминала
docker run -d --rm --name ydb -p 2136:2136 -p 8765:8765 \
cr.yandex/yc/yandex-docker-local-ydb:latest

Подключись через ydb -e grpc://localhost:2136 -d /local, создай таблицу, селект.

Загрузи 100M строк логов в обе БД, замерь:

  • Time to ingest.
  • SELECT count(*) WHERE date BETWEEN ....
  • SELECT service, count() GROUP BY service.

Должна быть разница 10-100x в пользу CH.


  1. CockroachDB Architecture. https://www.cockroachlabs.com/docs/stable/architecture/overview.html
  2. CockroachDB on Go workload. https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html
  3. YugabyteDB Docs. https://docs.yugabyte.com/
  4. YDB Documentation. https://ydb.tech/docs/
  5. ydb-go-sdk. https://github.com/ydb-platform/ydb-go-sdk
  6. TiDB Docs. https://docs.pingcap.com/tidb/stable/
  7. ClickHouse Docs. https://clickhouse.com/docs
  8. ClickHouse Internals (blog series). https://clickhouse.com/blog
  9. ClickHouse Go Driver. https://github.com/ClickHouse/clickhouse-go
  10. Tarantool Docs. https://www.tarantool.io/en/doc/latest/
  11. go-tarantool. https://github.com/tarantool/go-tarantool
  12. Vshard. https://www.tarantool.io/en/doc/latest/reference/reference_rock/vshard/
  13. Spanner paper (Google). https://research.google/pubs/spanner-googles-globally-distributed-database/
  14. OpenTelemetry + ClickHouse. https://signoz.io/blog/clickhouse-vs-elasticsearch/
  15. «Designing Data-Intensive Applications» — Martin Kleppmann. Главы о distributed transactions, replication.

Projection — это «второй порядок сортировки» поверх той же таблицы. Полезно, когда нужно эффективно фильтровать/агрегировать по разным колонкам без создания отдельных MV.

ALTER TABLE events
ADD PROJECTION p_by_type
(SELECT * ORDER BY (event_type, ts));
ALTER TABLE events MATERIALIZE PROJECTION p_by_type;

После — SELECT … WHERE event_type = 'click' идёт по проекции (быстро) даже если основной ORDER BY был (user_id, ts).

⚠️ Projections занимают место. Контролируй через system.projection_parts.

CREATE TABLE events (
ts DateTime,
payload String
)
ENGINE = MergeTree
ORDER BY ts
TTL
ts + INTERVAL 7 DAY TO VOLUME 'cold',
ts + INTERVAL 30 DAY TO DISK 's3_cold',
ts + INTERVAL 90 DAY DELETE;

Storage policy в config.xml:

<storage_configuration>
<disks>
<s3_cold>
<type>s3</type>
<endpoint>https://s3.amazonaws.com/bucket/cold/</endpoint>
<access_key_id>...</access_key_id>
<secret_access_key>...</secret_access_key>
</s3_cold>
</disks>
<policies>
<tiered>
<volumes>
<hot><disk>default</disk></hot>
<cold><disk>s3_cold</disk></cold>
</volumes>
</tiered>
</policies>
</storage_configuration>

ClickHouse сам перекладывает old parts на дешёвый storage.

ClickHouse 21.8+ предоставляет ClickHouse Keeper — drop-in replacement ZooKeeper с тем же ZK wire-protocol, но написан внутри CH. Преимущества:

  • Один deployment process (нет отдельной JVM ZK).
  • Лучше производительность (~3x на latency).
  • Tested внутри CH community.
<!-- keeper_server section -->
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>1</server_id>
<coordination_settings>
<session_timeout_ms>30000</session_timeout_ms>
</coordination_settings>
<raft_configuration>
<server><id>1</id><hostname>ch1</hostname><port>9234</port></server>
<server><id>2</id><hostname>ch2</hostname><port>9234</port></server>
<server><id>3</id><hostname>ch3</hostname><port>9234</port></server>
</raft_configuration>
</keeper_server>

ClickHouse имеет встроенный profiler:

SET allow_introspection_functions=1, query_profiler_real_time_period_ns=10000000;
SELECT … (медленный запрос);
SELECT trace, count() FROM system.trace_log
WHERE query_id='' GROUP BY trace ORDER BY count() DESC LIMIT 20;

system.query_log — все запросы с их временем, памятью, прочитанными rows / bytes.

ClickHouse Replicated*MergeTree:

  • Async by default — INSERT возвращается, как только запись попала в лог на ZK/Keeper.
  • insert_quorum — ждать, пока N реплик подтвердят.
SET insert_quorum = 2, insert_quorum_timeout = 600000;
INSERT INTO events SELECT …;

Это аналог Kafka acks=all. Latency растёт, durability — выше.

  • Транзакции, OLTP, точечные UPDATE/DELETE → Postgres / CRDB.
  • Tiny aggregates на маленьких таблицах → Postgres.
  • Joins по большим таблицам без явного ключа → ClickHouse слаб на сложных JOIN’ах.
  • Низкая cardinality + малый объём → overkill.

clickhouse-backup — open-source tool. Делает inkremental backup на S3.

Окно терминала
clickhouse-backup create my_backup_20260520
clickhouse-backup upload my_backup_20260520
clickhouse-backup download my_backup_20260520
clickhouse-backup restore my_backup_20260520

Tarantool имеет vinyl engine (помимо memtx) — LSM-tree, данные не обязательно в RAM. Trade-off:

  • memtx: latency 10us, ограничено RAM.
  • vinyl: latency 100us-1ms, не ограничено диском.

Используется, когда объём > RAM, но нужна Tarantool API.

Vshard — Lua-библиотека для горизонтального масштабирования:

┌────────────┐
│ Router │ (логика hash(key) → bucket → replicaset)
└─────┬──────┘
┌───────────┴────────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ Replicaset 1 │ │ Replicaset 2 │
│ (master+ │ │ (master+ │
│ replica) │ │ replica) │
└──────────┘ └──────────┘
  • Bucket — единица перебалансировки.
  • На rebalance — буckets перемещаются между replicasets без остановки.
  • Default: 3000 buckets per cluster.
СценарийЛучший выбор
Финтех с multi-region (zero data loss)CockroachDB / YDB
Logs / observability на TB/деньClickHouse
Real-time ad tech / analyticsClickHouse / Pulsar+CH
Sessions / hot cacheTarantool / Redis
Replace MySQL (sharded)TiDB / Yugabyte
Embedded analyticsDuckDB
Postgres outgrew vertical scalingCRDB / Citus / YDB