На этой странице мы используем термины “ключ упорядочивания” и “первичный ключ” как взаимозаменяемые. Строго говоря, в ClickHouse это разные понятия, но в рамках этого документа их можно считать взаимозаменяемыми; при этом под ключом упорядочивания имеются в виду столбцы, указанные в ORDER BY таблицы.
Обратите внимание, что первичный ключ в ClickHouse работает совсем иначе, чем аналогичные понятия в OLTP-базах данных, таких как Postgres.
Выбор эффективного первичного ключа в ClickHouse критически важен для производительности запросов и эффективности хранения. ClickHouse организует данные в части, каждая из которых содержит собственный разреженный первичный индекс. Этот индекс значительно ускоряет выполнение запросов, сокращая объём сканируемых данных. Кроме того, поскольку первичный ключ задаёт физический порядок данных на диске, он напрямую влияет на эффективность сжатия. Оптимально упорядоченные данные сжимаются лучше, что дополнительно повышает производительность за счёт снижения I/O.
- При выборе ключа упорядочивания отдавайте приоритет столбцам, которые часто используются в фильтрах запросов (то есть в условии
WHERE), особенно если они позволяют исключить большое количество строк. - Также полезны столбцы, сильно коррелирующие с другими данными в таблице, поскольку последовательное хранение улучшает коэффициент сжатия и эффективность использования памяти при операциях
GROUP BYиORDER BY.
При выборе ключа упорядочивания можно руководствоваться несколькими простыми правилами. Иногда они могут противоречить друг другу, поэтому учитывайте их именно в таком порядке. С помощью этого подхода можно определить несколько ключей; обычно достаточно 4–5:
ВажноКлючи упорядочивания должны задаваться при создании таблицы и не могут быть добавлены позже. Дополнительное упорядочивание можно добавить в таблицу после (или до) вставки данных с помощью возможности, известной как проекции. Имейте в виду, что это приводит к дублированию данных. Подробнее здесь.
Пример
posts_unordered. В ней содержится по одной строке для каждого поста Stack Overflow.
У этой таблицы нет первичного ключа — на это указывает ORDER BY tuple().
EXPLAIN indexes=1 подтверждает, что из-за отсутствия индексации выполняется полное сканирование таблицы.
posts_ordered, содержащая те же данные, определена с ORDER BY вида (PostTypeId, toDate(CreationDate)), то есть
PostTypeId имеет мощность 8 и является логичным выбором для первого элемента нашего ключа упорядочивания. Поскольку фильтрации с точностью до даты, скорее всего, будет достаточно (при этом она по-прежнему будет полезна и для фильтров по DateTime), мы используем toDate(CreationDate) в качестве второго компонента нашего ключа. Это также позволит уменьшить размер индекса, поскольку дату можно представить 16 битами, что ускоряет фильтрацию.
Следующая анимация показывает, как создается оптимизированный разреженный первичный индекс для таблицы постов Stack Overflow. Вместо индексации отдельных строк индекс строится по блокам строк:
Если повторить тот же запрос для таблицы с этим ключом упорядочивания:
EXPLAIN indexes=1.
Все столбцы в таблице будут отсортированы по значению указанного ключа упорядочивания, независимо от того, входят ли они в сам ключ. Например, если в качестве ключа используется
CreationDate, порядок значений во всех остальных столбцах будет соответствовать порядку значений в столбце CreationDate. Можно указать несколько ключей упорядочивания — в этом случае сортировка будет выполняться с той же семантикой, что и в предложении ORDER BY запроса SELECT.