ClickHouse — прод: вставка, бэкапы, дискClickHouse — production: ingestion, backups, disk
Правильно спроектированная схема (см. проектирование) может всё равно упасть в проде из-за паттерна вставки. Здесь — как батчить запись, чем бэкапить кластер, и как совмещать TTL с холодным хранилищем, не платя за горячий диск данными, которые никто не читает. Кластеризация — в отдельной главе «Настройка шардов», мониторинг — в «Мониторинге».A well-designed schema (see schema design) can still fall over in production because of the ingestion pattern. Here's how to batch writes, what to back up a cluster with, and how to combine TTL with cold storage without paying hot-disk prices for data nobody reads. Clustering lives in its own "Sharding" chapter, monitoring in "Monitoring."
Главное правило продовой эксплуатации ClickHouse: вставлять пачками, а не по одной строке (см. грабли в архитектуре — "Too many parts"). Ориентир — тысячи-десятки тысяч строк за один `INSERT`, не чаще раза в секунду на таблицу. Если источник данных — поток одиночных событий (Kafka, HTTP), между источником и ClickHouse нужен буфер, который накапливает строки и сбрасывает их пачкой: либо движок `Buffer` (промежуточная in-memory таблица перед целевой), либо буферизация на стороне приложения/коллектора перед вставкой.The cardinal rule of running ClickHouse in production: insert in batches, not one row at a time (see the "Too many parts" gotcha in architecture). Aim for thousands to tens of thousands of rows per INSERT, no more than about once a second per table. If the data source is a stream of individual events (Kafka, HTTP), a buffer is needed between the source and ClickHouse that accumulates rows and flushes them as a batch: either the Buffer engine (an intermediate in-memory table in front of the target) or buffering on the application/collector side before inserting.
Асинхронные вставки (`async_insert = 1`) — встроенная альтернатива самостоятельному буферу: сервер сам накапливает мелкие вставки от множества клиентов в один более крупный кусок перед записью на диск, ценой небольшой задержки подтверждения. Полезно, когда источников много и каждый пишет редко и понемногу — типичная ситуация для коллекторов метрик с большим количеством независимых инстансов.Async inserts (async_insert = 1) are a built-in alternative to a hand-rolled buffer: the server itself accumulates small inserts from many clients into one larger part before writing to disk, at the cost of a small acknowledgment delay. Useful when there are many sources each writing rarely and in small amounts — a typical situation for metric collectors with many independent instances.
Штатный `BACKUP`/`RESTORE` (начиная с недавних версий) и сторонний `clickhouse-backup` оба опираются на инкрементальность на уровне кусков — поскольку куски неизменяемы (см. архитектуру), бэкап новой версии копирует только новые/изменённые куски, а не всю таблицу целиком, что делает регулярные бэкапы больших таблиц практически осуществимыми. Хранить бэкапы имеет смысл там же, где и tiered cold-хранилище (S3-совместимое объектное хранилище) — оба сценария используют одну и ту же интеграцию ClickHouse с object storage.Both the built-in BACKUP/RESTORE (in recent versions) and the third-party clickhouse-backup rely on part-level incrementality — since parts are immutable (see architecture), backing up a new version copies only new/changed parts, not the whole table, which makes regular backups of large tables practically feasible. It makes sense to store backups in the same place as tiered cold storage (S3-compatible object storage) — both scenarios use the same ClickHouse integration with object storage.
`TTL` на уровне таблицы или колонки задаёт, что делать со старыми данными автоматически: удалить (`TTL ts + INTERVAL 90 DAY DELETE`), пересжать сильнее (`... RECOMPRESS CODEC(ZSTD(9))`) или перенести на другой storage-policy (`... TO VOLUME 'cold'`) — последнее и есть механизм многоуровневого (tiered) хранения: горячие данные на быстром NVMe, данные старше порога — на дешёвом, но медленном объектном хранилище (S3-совместимом, включая self-hosted MinIO), прозрачно для запросов — движок сам знает, где физически лежит каждый кусок.TTL at the table or column level defines what to do with old data automatically: delete it (TTL ts + INTERVAL 90 DAY DELETE), recompress it harder (... RECOMPRESS CODEC(ZSTD(9))), or move it to a different storage policy (... TO VOLUME 'cold') — the last is the tiered-storage mechanism itself: hot data on fast NVMe, data past a threshold on cheap but slow object storage (S3-compatible, including self-hosted MinIO), transparently to queries — the engine itself knows where each part physically lives.
ALTER TABLE trades
MODIFY TTL
ts + INTERVAL 30 DAY RECOMPRESS CODEC(ZSTD(9)),
ts + INTERVAL 180 DAY TO VOLUME 'cold',
ts + INTERVAL 2 YEAR DELETE;
Разница с полностью внешним подходом (например, старый снапшот-коллектор стакана, который писал прямо в ClickHouse и требовал отдельного внешнего процесса для архивации) в том, что TTL — декларативное правило внутри самой БД: не нужен внешний cron/скрипт, который сам решает, что и когда переносить, — движок делает это как часть штатного фонового цикла слияний.The difference from a fully external approach (say, an old order-book snapshot collector that wrote straight into ClickHouse and needed a separate external process for archiving) is that TTL is a declarative rule inside the DB itself: no external cron/script deciding what to move and when — the engine does it as part of its regular background merge cycle.
Если 50 независимых коллекторов пишут по одной-две строки в секунду каждый напрямую в ClickHouse, суммарно это всё равно поток мелких `INSERT`, даже если каждый источник "батчит" свои единичные строки — батчинг должен происходить на общей точке перед ClickHouse (`Buffer`-таблица, Kafka + `async_insert`, промежуточный агрегатор), а не на каждом источнике по отдельности.If 50 independent collectors each write one or two rows per second directly to ClickHouse, the aggregate is still a stream of small inserts even if each source "batches" its own single rows — batching needs to happen at a shared point in front of ClickHouse (a Buffer table, Kafka + async_insert, an intermediate aggregator), not separately at each source.
Перенос куска на холодный том по TTL происходит в рамках фонового цикла, а не мгновенно по истечении срока — на нагруженной системе с большим количеством кусков возможна задержка, в течение которой "просроченные" данные всё ещё занимают дорогое горячее хранилище. Мониторинг свободного места на горячем томе (через `system.disks`) должен учитывать этот лаг, а не полагаться на TTL как на гарантию мгновенного освобождения места.Moving a part to a cold volume via TTL happens within the background cycle, not instantly on expiry — under load with many parts, there can be a delay during which "expired" data still occupies expensive hot storage. Monitoring free space on the hot volume (via system.disks) should account for this lag rather than treating TTL as a guarantee of immediate space reclamation.
Если коллектор использует бинарный формат вставки (например, Protobuf) и путь к файлу схемы для десериализации не настроен, ClickHouse может принять вставку без явной ошибки, но записать пустые/нулевые значения вместо реальных данных — сбой тихий и обнаруживается только на этапе чтения результата, а не на этапе записи. Явная проверка соответствия схемы формата (не полагаться на молчаливый успех вставки) — обязательная часть проверки перед деплоем коллектора.If a collector uses a binary insert format (say, Protobuf) and the schema-file path for deserialization isn't configured, ClickHouse can accept the insert without an explicit error but write empty/zero values instead of real data — a silent failure only caught when reading the result back, not at write time. Explicitly verifying the format schema is wired up (not trusting a silently successful insert) is a mandatory pre-deploy check for a collector.