Адаптивный пересказ статьи A Quadrillion Rows across three Clouds: scaling LogHouse из блога ClickHouse. Текст мой, цифры — из оригинала.
Полезность этой истории не в самих числах, а в том, что решения на таком масштабе видны выпукло: там, где на маленькой инсталляции ошибка стоит пару процентов, здесь она стоит остановки конвейера.
Порядок величин для контекста:
| Объём (несжатый) | 431 ПиБ |
| Строк | 1.59 квадриллиона |
| Геошарды / ячейки | 33 / 36 в 30+ регионах |
| Пик приёма | 80 ГиБ/с, 190 млн строк/с |
| Сжатие | 16× (431 ПиБ → 27 ПиБ на диске) |
| Хранение | 180 дней (часть — 365) |
Любопытная деталь: 78% объёма и 93% строк — это телеметрия из системных таблиц (тот самый SysEx из части 12), а не логи приложений.
Запись: геошардинг
Данные пишутся в том же регионе, где родились. Это не про производительность, а про деньги: межрегиональный трафик в облаках стоит дорого, и на таких объёмах он бы доминировал в счёте.
Несколько ячеек на регион дают горизонтальное масштабирование, не задевая другие регионы — важное свойство для изоляции аварий.
Async inserts и размер кусков
Проблема мелких вставок, разобранная в части 6, на таком потоке становится определяющей. Решение — серверный батчинг: ClickHouse копит строки у себя, пока не сработает порог.
Эффект измеримый:
- для
otel_logs— примерно 24-кратный батчинг; - для
text_log— около 4-кратного; - на выходе куски по 195–311 МиБ вместо тысяч крошечных.
Это ровно та разница между «слияния не успевают, ловим too many parts» и «система работает».
Суточные партиции вместо месячных
Контринтуитивный момент. В части 5 общая рекомендация — не дробить партиции слишком мелко, обычно toYYYYMM(ts). Здесь пошли в другую сторону — на сутки, и вот почему:
- при 180-дневном TTL получается около 180 активных партиций — вполне обозримое число;
- TTL срабатывает чистым
DROPпартиции, а не дорогим удалением строк; - в больших таблицах месячные партиции приводили к неконтролируемому росту числа кусков, и слияния начинали захлёбываться.
Правило, которое из этого следует: размер партиции нужно выбирать под срок хранения и объём, а не по привычке. Ориентир прежний — сотни активных партиций, а не тысячи и не единицы.
Надёжность приёма
Неудавшиеся батчи OTel сначала копятся в памяти, а при затяжном сбое сливаются в S3. Формулировка результата точная: это создаёт задержку приёма, а не потерю данных. Подробно эта схема разобрана в части 11.
Чтение: три уровня Distributed
Самое переносимое инженерное решение из всей статьи. Пользователь не должен знать про ячейки, регионы и облака — поэтому топология спрятана за иерархией таблиц:
flowchart TD
Q["Запрос"] --> L1["Уровень 1: глобальная Distributed<br/>все регионы"]
L1 --> L2A["Уровень 2: региональная Distributed<br/>ячейки региона A"]
L1 --> L2B["Уровень 2: региональная Distributed<br/>ячейки региона B"]
L2A --> L3A[("Уровень 3: SharedMergeTree<br/>данные")]
L2B --> L3B[("Уровень 3: SharedMergeTree<br/>данные")]
- Уровень 3 —
SharedMergeTree, собственно данные в ячейке. - Уровень 2 — региональная
Distributedповерх ячеек своего региона. - Уровень 1 — глобальная
Distributedповерх всех регионов.
Ключ к производительности глобального уровня — optimize_skip_unused_shards вместе со словарём, отображающим регион в идентификатор шарда. Благодаря этому запрос с фильтром по региону не идёт в ненужные ячейки.
Результат:
- запрос с фильтром по региону — 270–900 мс;
- запрос по всему парку, сканирующий 50 ГБ по всем 36 ячейкам — около 1.5 с.
Что забрать себе
Даже на инсталляции в тысячу раз меньше работают те же принципы:
- Держите запись локальной к источнику данных — межрегиональный трафик дорог и на средних объёмах.
- Async inserts не опция, а необходимость для потоковой телеметрии: смотрите на итоговый размер кусков, а не на число вставок.
- Гранулярность партиций подбирайте под TTL. Цель — чтобы удаление старого было
DROP PARTITION, а число активных партиций оставалось в сотнях. - Прячьте топологию за Distributed-иерархией и обязательно включайте отсечение ненужных шардов — иначе каждый запрос идёт во все ячейки.
- Сбой приёма должен превращаться в задержку, а не в потерю.
На этом серия завершается. Начало — в части 1, практические основы эксплуатации — в части 10.