deшка
Урок 10.2

Строковое и колоночное хранение

Какие данные читает ClickHouse и как блоки значений проходят фильтрацию и агрегацию.

Таблица выглядит одинаково в SQL-клиенте независимо от устройства хранения. Отличие проявляется, когда база читает данные. В строковом представлении рядом находятся значения одной записи. В колоночном — последовательности значений одного столбца.

Что меняет расположение значений

Пусть таблица logs содержит источник source, статус status, количество обработанных строк rows_loaded, время, идентификатор и длинный текст сообщения. Для суммы по источникам нужна лишь часть полей:

SELECT source, sum(rows_loaded) AS loaded
FROM logs
WHERE status = 'ok'
GROUP BY source
ORDER BY source;

Читать идентификатор, время и текст сообщения для этого результата не требуется. Колонка статуса нужна, хотя она не выводится: по ней применяется WHERE. В строковом хранении ненужные поля могут оказаться в тех же дисковых блоках, что и нужные. Колоночное хранение позволяет выбирать потоки данных по столбцам. Чем шире таблица и меньше выбранная доля столбцов, тем существеннее эта разница.

Три одинаковые записи: целые строки подряд и отдельные потоки id, source и value. Для суммы выделен поток value.

На схеме одни и те же три записи. В строковом варианте поле value перемежается с id и source. В колоночном его значения собраны в отдельный поток: для суммы достаточно прочитать 100, 200 и 50. Другие поля понадобятся только при соответствующих фильтрах или выводе.

Схема показывает логическое расположение значений. Физические файлы зависят от формата части MergeTree: Wide хранит колонки раздельно, Compact объединяет их в одном файле.

Как проходит запрос

Перед вычислением ClickHouse анализирует SQL и строит план: откуда читать, какие фильтры применить, что агрегировать и как упорядочить результат. Если ключ позволяет исключить диапазоны, часть данных не попадает в чтение.

Для выбранных диапазонов база читает сжатые данные нужных столбцов и распаковывает их. Затем проверяет status = 'ok', оставляет соответствующие значения source и rows_loaded и накапливает сумму для каждого источника. Последний ORDER BY source упорядочивает уже небольшой результат.

Векторное выполнение обрабатывает блок значений одной операцией: например, сравнивает массив статусов с 'ok'. Некоторые вычисления дополнительно используют SIMD — инструкции процессора, обрабатывающие несколько значений за раз. SIMD ускоряет отдельные операции внутри векторного выполнения.

Что дают несколько ядер

Параллельная обработка делит чтение и вычисление между несколькими потоками. Каждый обрабатывает свой диапазон и собирает промежуточные суммы. После этого состояния объединяются. Для суммы всё просто: суммы 500 и 700 дают 1200. Для среднего нужно сохранить и сумму, и число значений, а не усреднить два частичных средних.

Настройка max_threads ограничивает параллелизм запроса. Фактическое число потоков зависит от объёма работы; ускорение также ограничивают диск, пропускная способность памяти и одновременные запросы.

Стоимость запроса

У SELECT * исчезает выигрыш от выбора небольшой части столбцов. У группировки по уникальному идентификатору состояние почти столь же велико, как вход. Сложное преобразование строк может упереться в CPU. Большой JOIN требует построить структуру для соединения или использовать иной алгоритм.

Хороший ключ сокращает чтение диапазонов. Подходящий алгоритм и подготовленная витрина сокращают вычисления после чтения.

Войти, чтобы отмечатьдальше →
10.3 →Типы данных: точность, отсутствие и повторяемость