ClickHouse/docs/ru/dicts/external_dicts_dict_layout.md

292 lines
15 KiB
Markdown
Raw Normal View History

<a name="dicts-external_dicts_dict_layout"></a>
# Хранение словарей в памяти
2018-03-14 09:08:37 +00:00
Словари можно размещать в памяти [множеством способов](external_dicts_dict_layout#dicts-external_dicts_dict_layout-manner).
2018-03-14 09:08:37 +00:00
Рекомендуем [flat](external_dicts_dict_layout#dicts-external_dicts_dict_layout-flat), [hashed](external_dicts_dict_layout#dicts-external_dicts_dict_layout-hashed) и [complex_key_hashed](external_dicts_dict_layout#dicts-external_dicts_dict_layout-complex_key_hashed). Скорость обработки словарей при этом максимальна.
2018-03-14 09:08:37 +00:00
Размещение с кэшированием не рекомендуется использовать из-за потенциально низкой производительности и сложностей в подборе оптимальных параметров. Читайте об этом подробнее в разделе " [cache](external_dicts_dict_layout#dicts-external_dicts_dict_layout-cache)".
Повысить производительнось словарей можно следующими способами:
- Вызывать функцию для работы со словарём после `GROUP BY`.
- Помечать извлекаемые атрибуты как инъективные. Атрибут называется инъективным, если разным ключам соответствуют разные значения атрибута. Тогда при использовании в `GROUP BY` функции, достающей значение атрибута по ключу, эта функция автоматически выносится из `GROUP BY`.
При ошибках работы со словарями ClickHouse генерирует исключения. Например, в следующих ситуациях:
- При обращении к словарю, который не удалось загрузить.
- При ошибке запроса к `cached`-словарю.
Список внешних словарей и их статус можно посмотреть в таблице `system.dictionaries`.
Общий вид конфигурации:
```xml
2017-11-19 12:07:08 +00:00
<yandex>
<dictionary>
...
<layout>
2018-03-10 23:36:26 +00:00
<layout_type>
<!-- layout settings -->
</layout_type>
</layout>
...
</dictionary>
2017-11-19 12:07:08 +00:00
</yandex>
```
<a name="dicts-external_dicts_dict_layout-manner"></a>
## Способы размещения словарей в памяти
- [flat](#dicts-external_dicts_dict_layout-flat)
- [hashed](#dicts-external_dicts_dict_layout-hashed)
- [cache](#dicts-external_dicts_dict_layout-cache)
- [range_hashed](#dicts-external_dicts_dict_layout-range_hashed)
- [complex_key_hashed](#dicts-external_dicts_dict_layout-complex_key_hashed)
- [complex_key_cache](#dicts-external_dicts_dict_layout-complex_key_cache)
2018-03-14 09:08:37 +00:00
- [ip_trie](#dicts-external_dicts_dict_layout-ip_trie)
<a name="dicts-external_dicts_dict_layout-flat"></a>
### flat
Словарь полностью хранится в оперативной памяти в виде плоских массивов. Объем памяти, занимаемой словарем? пропорционален размеру самого большого (по размеру) ключа.
Ключ словаря имеет тип `UInt64` и его величина ограничена 500 000. Если при создании словаря обнаружен ключ больше, то ClickHouse бросает исключение и не создает словарь.
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
Это метод обеспечивает максимальную производительность среди всех доступных способов размещения словаря.
Пример конфигурации:
```xml
<layout>
<flat />
</layout>
```
<a name="dicts-external_dicts_dict_layout-hashed"></a>
### hashed
Словарь полностью хранится в оперативной памяти в виде хэш-таблиц. Словарь может содержать произвольное количество элементов с произвольными идентификаторами. На практике, количество ключей может достигать десятков миллионов элементов.
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
Пример конфигурации:
```xml
<layout>
<hashed />
</layout>
```
<a name="dicts-external_dicts_dict_layout-complex_key_hashed"></a>
### complex_key_hashed
2018-03-14 09:08:37 +00:00
Тип размещения предназначен для использования с составными [ключами](external_dicts_dict_structure#dicts-external_dicts_dict_structure). Аналогичен `hashed`.
Пример конфигурации:
```xml
<layout>
<complex_key_hashed />
</layout>
```
<a name="dicts-external_dicts_dict_layout-range_hashed"></a>
### range_hashed
Словарь хранится в оперативной памяти в виде хэш-таблицы с упорядоченным массивом диапазонов и соответствующих им значений.
Этот способ размещения работает также как и hashed и позволяет дополнительно к ключу использовать дипазоны по дате/времени, если они указаны в словаре.
Пример: таблица содержит скидки для каждого рекламодателя в виде:
```
+------------------+-----------------------------+------------+----------+
| id рекламодателя | дата начала действия скидки | дата конца | величина |
+==================+=============================+============+==========+
| 123 | 2015-01-01 | 2015-01-15 | 0.15 |
+------------------+-----------------------------+------------+----------+
| 123 | 2015-01-16 | 2015-01-31 | 0.25 |
+------------------+-----------------------------+------------+----------+
| 456 | 2015-01-01 | 2015-01-15 | 0.05 |
+------------------+-----------------------------+------------+----------+
```
2018-03-14 09:08:37 +00:00
Чтобы использовать выборку по диапазонам дат, необходимо в [structure](external_dicts_dict_structure#dicts-external_dicts_dict_structure) определить элементы `range_min`, `range_max`.
Пример:
```xml
<structure>
<id>
<name>Id</name>
</id>
<range_min>
<name>first</name>
</range_min>
<range_max>
<name>last</name>
</range_max>
...
```
Для работы с такими словарями в функцию `dictGetT` необходимо передавать дополнительный аргумент - дату: :
dictGetT('dict_name', 'attr_name', id, date)
Функция возвращает значение для заданных `id` и диапазона дат, в который входит переданная дата.
Особенности алгоритма:
- Если не найден `id` или для найденного `id` не найден диапазон, то возвращается значение по умолчанию для словаря.
- Если есть перекрывающиеся диапазоны, то можно использовать любой подходящий.
- Если граница диапазона `NULL` или некорректная дата (1900-01-01, 2039-01-01), то диапазон считается открытым. Диапазон может быть открытым с обеих сторон.
Пример конфигурации:
```xml
2017-11-19 12:07:08 +00:00
<yandex>
<dictionary>
...
<layout>
<range_hashed />
</layout>
<structure>
<id>
<name>Abcdef</name>
</id>
<range_min>
<name>StartDate</name>
</range_min>
<range_max>
<name>EndDate</name>
</range_max>
<attribute>
<name>XXXType</name>
<type>String</type>
<null_value />
</attribute>
</structure>
</dictionary>
2017-11-19 12:07:08 +00:00
</yandex>
```
<a name="dicts-external_dicts_dict_layout-cache"></a>
### cache
Словарь хранится в кэше, состоящем из фиксированного количества ячеек. Ячейки содержат часто используемые элементы.
При поиске в словаре сначала просматривается кэш. На каждый блок данных, все не найденные в кэше или устаревшие ключи запрашиваются у источника с помощью `SELECT attrs... FROM db.table WHERE id IN (k1, k2, ...)`. Затем, полученные данные записываются в кэш.
Для cache-словарей может быть задано время устаревания (lifetime &lt;dicts-external_dicts_dict_lifetime&gt;) данных в кэше. Если от загрузки данных в ячейке прошло больше времени, чем `lifetime`, то значение не используется, и будет запрошено заново при следующей необходимости его использовать.
Это наименее эффективный из всех способов размещения словарей. Скорость работы кэша очень сильно зависит от правильности настройки и сценария использования. Словарь типа cache показывает высокую производительность лишь при достаточно больших hit rate-ах (рекомендуется 99% и выше). Посмотреть средний hit rate можно в таблице `system.dictionaries`.
Чтобы увеличить производительность кэша, используйте подзапрос с `LIMIT`, а снаружи вызывайте функцию со словарём.
2018-03-14 09:08:37 +00:00
Поддерживаются [источники](external_dicts_dict_sources#dicts-external_dicts_dict_sources): MySQL, ClickHouse, executable, HTTP.
Пример настройки:
```xml
<layout>
<cache>
<!-- Размер кэша в количестве ячеек. Округляется вверх до степени двух. -->
<size_in_cells>1000000000</size_in_cells>
</cache>
</layout>
```
Укажите достаточно большой размер кэша. Количество ячеек следует подобрать экспериментальным путём:
1. Выставить некоторое значение.
2. Запросами добиться полной заполненности кэша.
3. Оценить потребление оперативной памяти с помощью таблицы `system.dictionaries`.
4. Увеличивать/уменьшать количество ячеек до получения требуемого расхода оперативной памяти.
<div class="admonition warning">
Не используйте в качестве источника ClickHouse, поскольку он медленно обрабатывает запросы со случайным чтением.
</div>
<a name="dicts-external_dicts_dict_layout-complex_key_cache"></a>
### complex_key_cache
2018-03-14 09:08:37 +00:00
Тип размещения предназначен для использования с составными [ключами](external_dicts_dict_structure#dicts-external_dicts_dict_structure). Аналогичен `cache`.
<a name="dicts-external_dicts_dict_layout-ip_trie"></a>
### ip_trie
Тип размещения предназначен для сопоставления префиксов сети (IP адресов) с метаданными, такими как ASN.
Пример: таблица содержит префиксы сети и соответствующие им номера AS и коды стран:
```
+-----------------+-------+--------+
| prefix | asn | cca2 |
+=================+=======+========+
| 202.79.32.0/20 | 17501 | NP |
+-----------------+-------+--------+
| 2620:0:870::/48 | 3856 | US |
+-----------------+-------+--------+
| 2a02:6b8:1::/48 | 13238 | RU |
+-----------------+-------+--------+
| 2001:db8::/32 | 65536 | ZZ |
+-----------------+-------+--------+
```
При использовании такого макета структура должна иметь составной ключ.
Пример:
```xml
<structure>
<key>
<attribute>
<name>prefix</name>
<type>String</type>
</attribute>
</key>
<attribute>
<name>asn</name>
<type>UInt32</type>
<null_value />
</attribute>
<attribute>
<name>cca2</name>
<type>String</type>
<null_value>??</null_value>
</attribute>
...
```
Этот ключ должен иметь только один атрибут типа String, содержащий допустимый префикс IP. Другие типы еще не поддерживаются.
Для запросов необходимо использовать те же функции (`dictGetT` с кортежем), что и для словарей с составными ключами:
dictGetT('dict_name', 'attr_name', tuple(ip))
Функция принимает либо UInt32 для адреса IPv4, либо FixedString(16) для адреса IPv6:
dictGetString('prefix', 'asn', tuple(IPv6StringToNum('2001:db8::1')))
Никакие другие типы не поддерживаются. Функция возвращает атрибут для префикса, соответствующего данному IP-адресу. Если есть перекрывающиеся префиксы, возвращается наиболее специфический.
Данные хранятся в побитовом дереве (trie), он должены полностью помещаться в оперативной памяти.