Производительность запросов временных рядов
После оптимизации хранения следующим шагом является улучшение производительности запросов.
В этом разделе рассматриваются две ключевые техники: оптимизация ключей ORDER BY
и использование материализованных представлений.
Мы увидим, как эти подходы могут сократить время выполнения запросов с секунд до миллисекунд.
Оптимизация ключей ORDER BY
Перед тем как пытаться применять другие оптимизации, вы должны оптимизировать ключ сортировки, чтобы убедиться, что ClickHouse выдает максимально быстрые результаты.
Выбор ключа в значительной степени зависит от запросов, которые вы собираетесь выполнять. Предположим, что большинство наших запросов фильтрует по колонкам project
и subproject
.
В этом случае будет разумно добавить их в ключ сортировки — так же как и колонку времени, так как мы также делаем запросы по времени:
Давайте создадим другую версию таблицы, которая имеет те же типы колонок, что и wikistat
, но сортируется по (project, subproject, time)
.
Теперь давайте сравним несколько запросов, чтобы получить представление о том, насколько важно наше выражение ключа сортировки для производительности. Обратите внимание, что мы не применяли наши предыдущие оптимизации типов данных и кодеков, поэтому любые различия в производительности запросов основаны только на порядке сортировки.
Запрос | (time) | (project, subproject, time) |
---|---|---|
2.381 сек | 1.660 сек | |
2.148 сек | 0.058 сек | |
2.192 сек | 0.012 сек | |
2.968 сек | 0.010 сек |
Материализованные представления
Другой вариант — использовать материализованные представления для агрегации и хранения результатов популярных запросов. Эти результаты можно запрашивать вместо исходной таблицы. Предположим, что следующий запрос выполняется довольно часто в нашем случае:
Создание материализованного представления
Мы можем создать следующее материализованное представление:
Заполнение целевой таблицы
Эта целевая таблица будет заполняться только при вставке новых записей в таблицу wikistat
, поэтому нам нужно выполнить некоторые действия по заполнению.
Самый простой способ сделать это — использовать оператор INSERT INTO SELECT
для непосредственной вставки в целевую таблицу материализованного представления используя запрос SELECT (преобразование) представления:
В зависимости от кардинальности исходного набора данных (у нас 1 миллиард строк!) это может быть подходом, требующим большого объема памяти. В качестве альтернативы, вы можете использовать вариант, который требует минимального объема памяти:
- Создание временной таблицы с движком таблицы Null
- Подключение копии обычно используемого материализованного представления к этой временной таблице
- Использование запроса INSERT INTO SELECT, копируя все данные из исходного набора данных в эту временную таблицу
- Удаление временной таблицы и временного материализованного представления.
С таким подходом строки из исходного набора данных копируются блоками в временную таблицу (которая не сохраняет ни одну из этих строк), и для каждого блока строк вычисляется частичное состояние, которое записывается в целевую таблицу, где эти состояния поэтапно сливаются в фоновом режиме.
Далее мы создадим материализованное представление для чтения из wikistat_backfill
и записи в wikistat_top
:
И затем наконец, мы заполним wikistat_backfill
из первоначальной таблицы wikistat
:
После завершения этого запроса мы можем удалить таблицу заполнения и материализованное представление:
Теперь мы можем запрашивать материализованное представление вместо исходной таблицы:
Наше улучшение производительности здесь является значительным. Ранее на вычисление ответа на этот запрос уходило чуть больше 2 секунд, а теперь всего 4 миллисекунды.