Перейти к основному содержимому
Перейти к основному содержимому

Словарь

Словарь в ClickHouse предоставляет представление данных в памяти в формате ключ-значение, оптимизируя запросы на поиск с супернизкой задержкой из различных внутренних и внешних источников.

Словари полезны для:

  • Улучшения производительности запросов, особенно при использовании с JOINs
  • Обогащения загружаемых данных на лету без замедления процесса загрузки

Ускорение соединений с помощью словаря

Словари можно использовать для ускорения определенного типа JOIN: LEFT ANY типа, где ключ соединения должен соответствовать атрибуту ключа основного хранилища ключ-значение.

Если это так, ClickHouse может использовать словарь для выполнения Прямого Соединения. Это самый быстрый алгоритм соединения ClickHouse и применим, когда основной движок таблицы для правой стороны таблицы поддерживает запросы ключ-значение с низкой задержкой. ClickHouse имеет три движка таблиц, предоставляющих это: Join (который по сути является предварительно рассчитанной хеш-таблицей), EmbeddedRocksDB и Dictionary. Мы опишем подход на основе словаря, но механика остается одинаковой для всех трех движков.

Алгоритм прямого соединения требует, чтобы правая таблица поддерживалась словарем, таким образом, данные для соединения из этой таблицы уже присутствуют в памяти в форме структуры данных ключ-значение с низкой задержкой.

Пример

Используя набор данных Stack Overflow, давайте ответим на вопрос: Какой самый спорный пост по SQL на Hacker News?

Мы определим спорный пост как пост, у которого почти одинаковое количество положительных и отрицательных голосов. Мы вычисляем это абсолютное различие, где значение, близкое к 0, означает большую спорность. Мы предположим, что пост должен иметь как минимум 10 положительных и отрицательных голосов - посты, по которым люди не голосуют, не являются очень спорными.

С нашими нормализованными данными этот запрос в данный момент требует JOIN с использованием таблиц posts и votes:

Используйте меньшие наборы данных с правой стороны JOIN: Этот запрос может показаться более многословным, чем необходимо, с фильтрацией по PostIds, происходящей как в внешнем, так и в подзапросах. Это оптимизация производительности, которая гарантирует быстрое время ответа запроса. Для оптимальной производительности всегда убедитесь, что правая сторона JOIN является меньшим набором данных и как можно меньше. Для получения советов по оптимизации производительности JOIN и понимания доступных алгоритмов мы рекомендуем эту серию блогов.

Хотя этот запрос быстрый, он зависит от того, чтобы мы написали JOIN осторожно для достижения хорошей производительности. В идеале, мы бы просто отфильтровали посты, содержащие "SQL", прежде чем рассматривать количество UpVote и DownVote для подмножества блогов, чтобы вычислить нашу метрику.

Применение словаря

Чтобы продемонстрировать эти концепции, мы используем словарь для наших данных голосования. Поскольку словари, как правило, хранятся в памяти (ssd_cache является исключением), пользователи должны уделять внимание размеру данных. Подтверждаем размер нашей таблицы votes:

Данные будут храниться в несжатом виде в нашем словаре, поэтому нам нужно как минимум 4 ГБ памяти, если мы хотим хранить все колонки (мы этого не будем) в словаре. Словарь будет реплицирован по нашему кластеру, поэтому это количество памяти должно быть зарезервировано для каждого узла.

В приведенном ниже примере данные для нашего словаря происходят из таблицы ClickHouse. Хотя это представляет собой самый распространенный источник словарей, поддерживается ряд источников, включая файлы, http и базы данных, включая Postgres. Как мы покажем, словари могут автоматически обновляться, предоставляя идеальный способ гарантировать, что маленькие наборы данных, подверженные частым изменениям, доступны для прямых соединений.

Нашему словарю нужен первичный ключ, по которому будут выполняться запросы. Это концептуально идентично первичному ключу транзакционной базы данных и должно быть уникальным. Наш запрос требует поиска по ключу соединения - PostId. В свою очередь, словарь должен быть заполнен общим количеством положительных и отрицательных голосов по каждому PostId из нашей таблицы votes. Вот запрос для получения этих данных словаря:

Чтобы создать наш словарь, требуется следующий DDL - обратите внимание на использование нашего предыдущего запроса:

В самоуправляемом OSS вышеуказанная команда должна быть выполнена на всех узлах. В ClickHouse Cloud словарь будет автоматически реплицирован на все узлы. Вышеуказанное было выполнено на узле ClickHouse Cloud с 64 ГБ оперативной памяти, заняв 36 секунд на загрузку.

Чтобы подтвердить объем памяти, потребляемой нашим словарем:

Теперь получение положительных и отрицательных голосов для конкретного PostId можно выполнить с помощью простой функции dictGet. Ниже мы получаем значения для поста 11227902:

Не только этот запрос гораздо проще, он также более чем в два раза быстрее! Это можно оптимизировать еще больше, загрузив в словарь только посты с более чем 10 положительными и отрицательными голосами и предварительно вычислив спорное значение.

Обогащение данных во время запроса

Словари можно использовать для поиска значений во время выполнения запроса. Эти значения могут быть возвращены в результатах или использованы в агрегациях. Предположим, мы создаем словарь для сопоставления идентификаторов пользователей с их местоположением:

Мы можем использовать этот словарь для обогащения результатов постов:

Аналогично нашему предыдущему примеру с соединением, мы можем использовать тот же словарь, чтобы эффективно определить, откуда происходит большинство постов:

Обогащение данных во время индексации

В приведенном выше примере мы использовали словарь во время выполнения запроса, чтобы убрать соединение. Словари также можно использовать для обогащения строк во время вставки. Это обычно уместно, если значение обогащения не меняется и существует во внешнем источнике, который можно использовать для заполнения словаря. В этом случае обогащение строки во время вставки избегает поиска словаря во время выполнения запроса.

Предположим, что Location пользователя в Stack Overflow никогда не меняется (в реальности это не так) - в частности, столбец Location таблицы users. Предположим, мы хотим выполнить аналитический запрос по таблице постов по местоположению. Это содержит UserId.

Словарь предоставляет сопоставление от идентификатора пользователя к местоположению, поддерживаемое таблицей users:

Мы исключаем пользователей с Id < 0, что позволяет нам использовать тип словаря Hashed. Пользователи с Id < 0 являются системными пользователями.

Чтобы использовать этот словарь во время вставки в таблицу постов, нам нужно изменить схему:

В приведенном выше примере Location объявлен как MATERIALIZED столбец. Это означает, что значение может быть предоставлено как часть запроса INSERT и всегда будет вычисляться.

ClickHouse также поддерживает DEFAULT столбцы (где значение может быть вставлено или рассчитано, если не предоставлено).

Чтобы заполнить таблицу, мы можем использовать обычный INSERT INTO SELECT из S3:

Теперь мы можем получить название местоположения, из которого происходит большинство постов:

Продвинутые темы словарей

Выбор LAYOUT словаря

Клаузула LAYOUT управляет внутренней структурой данных для словаря. Существуют различные варианты, и они документированы здесь. Некоторые советы по выбору правильной компоновки можно найти здесь.

Обновление словарей

Мы указали LIFETIME для словаря MIN 600 MAX 900. LIFETIME - это период обновления словаря, значения здесь вызывают периодическую перезагрузку через случайный интервал между 600 и 900 секундами. Этот случайный интервал необходим для распределения нагрузки на источник словаря при обновлении на большом количестве серверов. Во время обновлений старую версию словаря все еще можно запрашивать, при этом только начальная загрузка блокирует запросы. Обратите внимание, что установка (LIFETIME(0)) предотвращает обновление словарей. Словари можно принудительно перезагрузить с помощью команды SYSTEM RELOAD DICTIONARY.

Для источников баз данных, таких как ClickHouse и Postgres, вы можете настроить запрос, который будет обновлять словари только в случае, если они действительно изменились (ответ на запрос это определяет), а не с периодическим интервалом. Дополнительные подробности можно найти здесь.

Другие типы словарей

ClickHouse также поддерживает Иерархические, Полигональные и Словари регулярных выражений.

Дополнительное чтение