Фильтр не по префиксу ключа сортировки сканирует всю таблицу
Таблица в ClickHouse:
CREATE TABLE fct_events (
event_date Date,
client_id UInt64,
event_type LowCardinality(String),
amount Decimal(18, 2)
) ENGINE = MergeTree
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, client_id);
Отчёты по периодам работают быстро. Но запрос службы поддержки «вся история одного клиента» без ограничения по дате читает почти всю таблицу и идёт минутами:
SELECT * FROM fct_events WHERE client_id = 981234;
Почему так и что здесь уместно сделать?
- Разреженный первичный индекс работает по префиксу ключа сортировки: без условия на event_date гранулы не отсекаются. Помогут ограничение периода, skipping-индекс или проекция с порядком (client_id, event_date).
- Нужно построить обычный вторичный индекс по client_id: в MergeTree он устроен так же, как B-tree в PostgreSQL, и даёт точечный доступ к строкам по значению.
- Проблема во фрагментации: запрос вынужден обходить сотни активных кусков, и после OPTIMIZE TABLE ... FINAL они сольются, а поиск по client_id станет быстрым.
- Нужно добавить client_id в ключ партиционирования: данные каждого клиента лягут в отдельную партицию, и запрос прочитает ровно её.
