🧭 Почему primary index в ClickHouse — это не обычный primary key
🔥 Главная мысль
Очень частая ошибка — переносить мышление из PostgreSQL, MySQL или MS SQL в ClickHouse.
Кажется, что primary key везде должен означать одно и то же: уникальность строки + быстрый поиск конкретной записи.
Но в ClickHouse это не так.
В ClickHouse primary index — это не “индекс на каждую строку”, а разреженный индекс по гранулам данных.
Его задача — не найти одну запись как можно быстрее, а как можно раньше понять, какие большие куски данных можно вообще не читать.
Именно поэтому primary index в ClickHouse — это часть аналитической архитектуры, а не просто ограничение уникальности.
➕ Плюсы и минусы
🟢 Плюсы:
• хорошо работает на больших объёмах данных • экономит память по сравнению с классическими построчными индексами • ускоряет аналитические запросы за счёт отсечения ненужных гранул • хорошо подходит под OLAP-нагрузку, где читают миллионы строк, а не одну запись
Пример плюса: если у тебя запрос по диапазону дат, ClickHouse может сразу отбросить гранулы, в которых нужного периода точно нет.
🔴 Минусы:
• это не механизм уникальности как в классическом primary key • он не создан для точечного поиска одной строки по произвольному полю • если выбрать плохой ключ сортировки, пользы будет мало • он не покрывает все сценарии фильтрации, особенно текстовый поиск, regex и сложные проверки на вхождение
Пример минуса: если ты ждёшь, что ClickHouse мгновенно найдёт одну запись по user_id так же, как OLTP-база по b-tree индексу, то это часто будет ложное ожидание.
🧪 Живые примеры
Сначала коротко: primary index в ClickHouse хранит не значение для каждой строки, а min/max значения на гранулу.
Гранула — это блок строк что по умолчанию равна — 8192 строки.
Что это значит на практике:
• ClickHouse хранит данные блоками • для каждого блока понимает диапазон значений • если блок под условие не подходит — он пропускается • если подходит — читается уже сам блок данных
Пример для плюса: у тебя таблица событий за год, а запрос только за март. Если у гранулы диапазон дат целиком лежит в июле, её можно не читать вообще.
Пример для минуса: если в таблице нужен поиск одной конкретной строки по полю, которое не связано с порядком сортировки, primary index может почти не помочь.
Пример из жизни: представь огромный архив документов, где на коробках написан только диапазон дат, а не список всех файлов внутри. Ты не знаешь точное место каждого документа, но очень быстро понимаешь, какие коробки даже не нужно открывать.
Вот так и работает primary index в ClickHouse.
🏗 Архитектурная мысль
В больших компаниях primary index в ClickHouse рассматривают не как аналог primary key из OLTP, а как способ правильно организовать чтение данных.
Сначала думают не про уникальность, а про другое:
• по каким полям чаще всего фильтруют • как данные будут сортироваться при записи • какие диапазонные условия будут в WHERE • какие запросы должны отсекать максимум лишнего чтения
Почему это важно:
• ClickHouse работает на очень больших объёмах • классический построчный индекс был бы слишком дорогим по памяти • для аналитики важнее быстро отсечь ненужные куски данных, чем хранить “путь к каждой строке” • основной индекс тесно связан с ORDER BY и физическим порядком данных в MergeTree-таблице
Риски:
• думать, что PRIMARY KEY в ClickHouse гарантирует уникальность • выбирать ключ “как в OLTP” • не учитывать, что индекс работает по гранулам, а не по строкам • пытаться решать primary index’ом задачи текстового поиска или regex
✅ Вывод
Primary index в ClickHouse — это не “уникальный ключ строки”, а “механизм не читать лишние данные” ⚡️
✅ Подходит: для OLAP, витрин, BI, больших таблиц, диапазонных фильтров
❌ Не подходит: как замена классическому primary key из OLTP