Learning Platform
Глоссарий Troubleshooting
Урок 06.05 · 25 мин
Средний
DictionaryLayoutflathashedcacheip_triecomplex_key_hashedCREATE DICTIONARYLIFETIMESOURCE

Словари: layout-ы и конфигурация

External dictionaries — механизм ClickHouse для хранения справочных данных в оперативной памяти с point-lookup доступом. В отличие от обычных таблиц, словарь загружается в RAM целиком (или кэшируется) и обеспечивает O(1) доступ по ключу через функцию dictGet().

Типичные use case: обогащение фактов dimension-атрибутами (страна по IP, название города по коду, категория товара по product_id). Словарь заменяет JOIN с lookup-таблицей, давая прирост производительности до 25x.


CREATE DICTIONARY: DDL-синтаксис

CREATE DICTIONARY country_dict (
    code String,
    country_name String,
    continent String
)
PRIMARY KEY code
SOURCE(CLICKHOUSE(TABLE 'countries' DB 'default'))
LAYOUT(HASHED())
LIFETIME(MIN 300 MAX 600);

Ключевые секции:

  • PRIMARY KEY — столбец (или столбцы), по которому выполняется lookup. Определяет тип ключа для layout-а.
  • SOURCE — откуда загружать данные. ClickHouse периодически перечитывает source для обновления словаря.
  • LAYOUT — структура хранения в памяти. Определяет скорость lookup-а, потребление памяти и тип поддерживаемых ключей.
  • LIFETIME — интервал автообновления. ClickHouse случайно выбирает момент обновления в диапазоне [MIN, MAX] для распределения нагрузки между репликами.

SOURCE: откуда загружать данные

SourceСинтаксисКогда использовать
ClickHouse tableSOURCE(CLICKHOUSE(TABLE 'tbl' DB 'db'))Справочник в той же или удалённой инстанции
ClickHouse querySOURCE(CLICKHOUSE(QUERY 'SELECT ...'))Нужна трансформация или фильтрация
HTTPSOURCE(HTTP(URL 'https://...' FORMAT CSV))Внешний REST API или файл на CDN
FileSOURCE(FILE(PATH '/path/file.csv' FORMAT CSV))Локальный файл на сервере
MySQLSOURCE(MYSQL(HOST '...' PORT 3306 DB '...' TABLE '...'))Справочник в MySQL
PostgreSQLSOURCE(POSTGRESQL(HOST '...' PORT 5432 DB '...' TABLE '...'))Справочник в PostgreSQL
MongoDBSOURCE(MONGODB(HOST '...' COLLECTION '...'))Справочник в MongoDB

Наиболее распространённый: CLICKHOUSE(TABLE) — словарь из таблицы в том же кластере. CLICKHOUSE(QUERY) позволяет применять фильтры и трансформации при загрузке.


LIFETIME: автообновление словаря

-- Обновление каждые 300-600 секунд (5-10 минут)
LIFETIME(MIN 300 MAX 600)

-- Обновление отключено (данные загружаются один раз)
LIFETIME(0)

-- Обновление каждые 60 секунд (точный интервал)
LIFETIME(60)

ClickHouse случайно выбирает момент обновления в диапазоне [MIN, MAX]. Это распределяет нагрузку: если 10 реплик обновляют словарь, они не делают это одновременно. При LIFETIME(0) словарь загружается при старте сервера и никогда не обновляется.

TIP

Для production-словарей всегда указывайте MIN и MAX. LIFETIME(300) без MIN/MAX обновляет каждые 300 секунд на всех репликах одновременно — spike нагрузки на source.


10 layout-ов: структуры хранения в памяти

Словарные layout-ы: ключ, память, скорость
LayoutТип layout-а определяет структуру данных в памяти и тип поддерживаемого ключа
Тип ключаТип ключа: UInt64 (числовой), Composite (составной), IP (адрес/CIDR)
Память и скоростьМодель хранения в памяти и скорость point-lookup
flatflat: массив в памяти, индексированный по UInt64. Самый быстрый lookup (O(1) по индексу массива). Подходит для ключей 0..N до 500K записей. Ограничение: ключи должны быть bounded UInt64 (дыры в диапазоне расходуют память).
UInt64 (bounded)UInt64 (bounded, 0..N). Ключ = индекс массива.
Array, O(1), fastestМассив (lowest memory per key). Fastest: O(1) array index lookup. Идеален для маленьких справочников до 500K записей с плотными числовыми ключами.
hashedhashed: hash-таблица в памяти. Универсальный layout для числовых ключей любого диапазона. Быстрый lookup O(1) amortized. Больше памяти, чем flat (overhead hash-таблицы).
UInt64 (any)UInt64 (любой диапазон). Ключ хэшируется.
Hash, O(1), fastHash table (medium memory). Fast: O(1) amortized. Стандартный выбор для числовых ключей среднего размера (100K-10M).
sparse_hashedsparse_hashed: разреженная hash-таблица. Меньше памяти, чем hashed, но медленнее lookup. Подходит для больших словарей, где важнее экономия RAM, чем скорость.
UInt64 (any)UInt64. Та же модель ключа, меньше памяти.
Sparse hash, slowerSparse hash (less memory). Slower lookup. Подходит для memory-sensitive сценариев с миллионами ключей.
cachecache: LRU-кэш фиксированного размера. Не загружает все данные -- кэширует только запрашиваемые ключи. Подходит для огромных словарей, где обращаются к малой доле ключей (hot key pattern).
UInt64 (hot keys)UInt64. Только hot keys в памяти.
LRU, variable speedFixed-size LRU. Variable speed (cache hit = fast, miss = source query). Идеален для словарей с миллиардами записей и hot key pattern.
complex_key_hashedcomplex_key_hashed: hash-таблица с составным ключом (Tuple). Для словарей, где lookup по нескольким столбцам одновременно (country_code + city_code).
Composite (Tuple)Composite (Tuple). Несколько столбцов как один ключ.
Hash, O(1), fastHash table. Fast O(1). Стандартный выбор для составных ключей. Те же характеристики, что hashed, но с multi-column key.
ip_trieip_trie: Trie-структура для IP-адресов и CIDR-масок. Lookup по IP возвращает наиболее специфичный matching prefix (longest prefix match). Идеален для GeoIP, ASN lookup.
IP / CIDRIP address / CIDR. Longest prefix match.
Trie, O(prefix), fastTrie structure. Fast for CIDR lookups (O(prefix length)). Единственный layout для IP-геолокации и ASN-определения.

Подробнее о каждом layout-е

flat — массив

CREATE DICTIONARY status_dict (
    id UInt64,
    name String
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(TABLE 'statuses'))
LAYOUT(FLAT())
LIFETIME(MIN 300 MAX 600);

Ключ = индекс массива. id=5 хранится в array[5]. Если ключи идут от 0 до 500K без больших пропусков — flat оптимален. При ключе id=1000000000 массив выделит память на миллиард элементов (большинство пустых) — неэффективно.

hashed — hash-таблица

LAYOUT(HASHED())

Универсальный layout для числовых ключей. Подходит для любого диапазона UInt64. Стандартный выбор, когда flat неприменим (ключи разреженные или диапазон слишком велик).

sparse_hashed — разреженная hash-таблица

LAYOUT(SPARSE_HASHED())

Экономит 30-50% памяти по сравнению с hashed за счёт более медленного lookup. Подходит для словарей с миллионами записей, где критичен объём RAM.

cache — LRU-кэш

LAYOUT(CACHE(SIZE_IN_CELLS 1000000))

Кэширует только запрашиваемые ключи. SIZE_IN_CELLS определяет максимальное количество записей в кэше. Cache miss вызывает запрос к source — повышает latency. Подходит для словарей с миллиардами записей и skewed access pattern (20% ключей покрывают 80% запросов).

complex_key_hashed — составной ключ

CREATE DICTIONARY geo_dict (
    country_code String,
    city_code String,
    city_name String,
    population UInt32
)
PRIMARY KEY country_code, city_code
SOURCE(CLICKHOUSE(TABLE 'cities'))
LAYOUT(COMPLEX_KEY_HASHED())
LIFETIME(MIN 600 MAX 1200);

Lookup по составному ключу: dictGet('geo_dict', 'city_name', tuple('RU', 'MSK')). Используйте, когда один столбец не уникально идентифицирует запись.

complex_key_cache — составной ключ + LRU

LAYOUT(COMPLEX_KEY_CACHE(SIZE_IN_CELLS 500000))

Комбинация composite key и LRU cache. Для больших словарей с составными ключами и hot key pattern.

ip_trie — IP-геолокация

CREATE DICTIONARY geoip_dict (
    prefix String,   -- '192.168.0.0/16'
    country String,
    city String,
    asn UInt32
)
PRIMARY KEY prefix
SOURCE(CLICKHOUSE(TABLE 'geoip_ranges'))
LAYOUT(IP_TRIE())
LIFETIME(MIN 3600 MAX 7200);

Lookup: dictGet('geoip_dict', 'country', toIPv4('192.168.1.100')) — находит наиболее специфичный CIDR prefix, содержащий IP. Единственный layout для GeoIP и ASN определения.

range_hashed — диапазонные ключи

CREATE DICTIONARY exchange_rates (
    currency String,
    rate Float64,
    valid_from Date,
    valid_to Date
)
PRIMARY KEY currency
RANGE(MIN valid_from MAX valid_to)
SOURCE(CLICKHOUSE(TABLE 'rates_history'))
LAYOUT(RANGE_HASHED())
LIFETIME(MIN 300 MAX 600);

Lookup: dictGet('exchange_rates', 'rate', 'USD', toDate('2024-06-15')) — находит запись, где valid_from <= 2024-06-15 <= valid_to. Подходит для исторических данных с datе-range validity (курсы валют, тарифы, цены).

direct — без кэширования

LAYOUT(DIRECT())

Каждый вызов dictGet() выполняет запрос к source. Нет кэша, нет загрузки в RAM. Самый медленный, но данные всегда актуальны. Подходит для редких lookup-ов, где свежесть критична.

ssd_cache — SSD-кэш

LAYOUT(SSD_CACHE(
    BLOCK_SIZE 4096
    FILE_SIZE 16106127360
    PATH '/var/lib/clickhouse/ssd_cache/'
))

Двухуровневый кэш: hot keys в RAM (index), cold keys на SSD. Для очень больших словарей (сотни миллионов записей), не помещающихся в RAM.


Рекомендации по выбору layout-а

Максимальная производительность запросов:

  • flat — если ключи bounded UInt64 (0..N) и словарь до 500K записей
  • hashed — если ключи UInt64 любого диапазона
  • complex_key_hashed — если составной ключ

Экономия памяти:

  • sparse_hashed — для больших словарей с числовыми ключами
  • cache / ssd_cache — для огромных словарей с hot key pattern

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

  • ip_trie — для IP-геолокации и CIDR matching
  • range_hashed — для date-range lookups (курсы, тарифы)
  • direct — для always-fresh данных при редких запросах
TIP

Начинайте с hashed (или complex_key_hashed для составных ключей). Переходите на flat, если ключи bounded и словарь маленький. Переходите на cache/sparse_hashed, если словарь не помещается в RAM.


Ключевые выводы

  1. CREATE DICTIONARY определяет 4 секции: PRIMARY KEY (ключ lookup-а), SOURCE (откуда данные), LAYOUT (структура в памяти), LIFETIME (интервал обновления).
  2. LIFETIME(MIN, MAX) рандомизирует обновление между репликами. LIFETIME(0) — загрузка один раз без обновлений.
  3. flat — массив, самый быстрый, только для bounded UInt64 до 500K. hashed — hash-таблица, универсальный для числовых ключей. complex_key_hashed — для составных ключей.
  4. cache — LRU для огромных словарей с hot key pattern. ip_trie — для IP/CIDR. range_hashed — для date-range.
  5. Рекомендация по умолчанию: flat (маленькие), hashed (средние), complex_key_hashed (составной ключ) — лучшая производительность запросов.
Dictionary Encoding: кросс-форматный анализ Conformed dimensions и шина данных

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Инженер создаёт словарь для справочника статусов заказа (id UInt64 от 1 до 50, name String). Таблица содержит 50 записей. Какой LAYOUT обеспечит максимальную производительность lookup?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7