mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 01:22:04 +00:00
f17c3afef7
Other changes are made related to this new part.
346 lines
26 KiB
ReStructuredText
346 lines
26 KiB
ReStructuredText
.. _dicts-external_dicts:
|
||
|
||
Внешние словари
|
||
===============
|
||
|
||
Существует возможность подключать свои собственные словари из различных источников данных.
|
||
Источником данных для словаря может быть файл на локальной файловой системе, сервер ClickHouse, сервер MySQL, MongoDB или любой ODBC источник.
|
||
Словарь может полностью храниться в оперативке и периодически обновляться, или быть частично закэшированным в оперативке и динамически подгружать отсутствующие значения.
|
||
|
||
Конфигурация внешних словарей находится в отдельном файле или файлах, указанных в конфигурационном параметре :ref:`dictionaries_config <server_settings-dictionaries_config>`.
|
||
Этот параметр содержит абсолютный или относительный путь к файлу с конфигурацией словарей. Относительный путь - относительно директории с конфигурационным файлом сервера. Путь может содержать wildcard-ы \* и ? - тогда рассматриваются все подходящие файлы. Пример: ``dictionaries/*.xml``.
|
||
|
||
Конфигурация словарей, а также множество файлов с конфигурацией, может обновляться без перезапуска сервера. Сервер проверяет обновления каждые 5 секунд. То есть, словари могут подключаться динамически.
|
||
|
||
Создание словарей может производиться при старте сервера или при первом использовании. Это определяется конфигурационном параметром :ref:`dictionaries_lazy_load <server_settings-dictionaries_lazy_load>` (в основном конфигурационном файле сервера). Параметр не обязателен, по умолчанию - ``true``. Если true, то каждый словарь создаётся при первом использовании; если словарь не удалось создать - вызов функции, использующей словарь, кидает исключение. Если ``false``, то все словари создаются при старте сервера, и в случае ошибки, сервер завершает работу.
|
||
|
||
Конфигурационный файл словарей имеет вид:
|
||
|
||
.. code-block:: xml
|
||
|
||
<dictionaries>
|
||
<comment>Не обязательный элемент с любым содержимым; полностью игнорируется.</comment>
|
||
|
||
<!-- Можно задать произвольное количество разных словарей. -->
|
||
<dictionary>
|
||
<!-- Имя словаря. Под этим именем словарь будет доступен для использования. -->
|
||
<name>os</name>
|
||
|
||
<!-- Источник данных. -->
|
||
<source>
|
||
|
||
<!-- Источник - файл на локальной файловой системе. -->
|
||
<file>
|
||
<!-- Путь на локальной файловой системе. -->
|
||
<path>/opt/dictionaries/os.tsv</path>
|
||
<!-- С помощью какого формата понимать файл. -->
|
||
<format>TabSeparated</format>
|
||
</file>
|
||
|
||
<!-- или источник - таблица на сервере MySQL.
|
||
<mysql>
|
||
<!- - Эти параметры могут быть указаны как снаружи (общие для всех реплик), так и внутри конкретной реплики - ->
|
||
<port>3306</port>
|
||
<user>clickhouse</user>
|
||
<password>qwerty</password>
|
||
<!- - Можно указать от одной до произвольного количества реплик для отказоустойчивости. - ->
|
||
<replica>
|
||
<host>example01-1</host>
|
||
<priority>1</priority> <!- - Меньше значение - больше приоритет. - ->
|
||
</replica>
|
||
<replica>
|
||
<host>example01-2</host>
|
||
<priority>1</priority>
|
||
</replica>
|
||
<db>conv_main</db>
|
||
<table>counters</table>
|
||
</mysql>
|
||
-->
|
||
|
||
<!-- или источник - таблица на сервере ClickHouse.
|
||
<clickhouse>
|
||
<host>example01-01-1</host>
|
||
<port>9000</port>
|
||
<user>default</user>
|
||
<password></password>
|
||
<db>default</db>
|
||
<table>counters</table>
|
||
</clickhouse>
|
||
<!- - Если адрес похож на localhost, то запрос будет идти без сетевого взаимодействия.
|
||
Для отказоустойчивости, вы можете создать Distributed таблицу на localhost и прописать её. - ->
|
||
-->
|
||
|
||
<!-- Для <mysql> и <clickhouse> доступен атрибут <where>, позволяющий задать условие выбора
|
||
<clickhouse>
|
||
<host>example01-01-1</host>
|
||
<port>9000</port>
|
||
<user>default</user>
|
||
<password></password>
|
||
<db>default</db>
|
||
<table>ids</table>
|
||
<where>id=10</where>
|
||
</clickhouse>
|
||
-->
|
||
|
||
<!-- или источник - исполняемый файл. Если layout.cache - список нужных ключей будет записан в поток STDIN программы -->
|
||
<executable>
|
||
<!-- Путь или имя программы (если директория есть в переменной окружения PATH) и параметры -->
|
||
<command>cat /opt/dictionaries/os.tsv</command>
|
||
<!-- С помощью какого формата понимать вывод и формировать список ключей. -->
|
||
<format>TabSeparated</format>
|
||
</executable>
|
||
|
||
<!-- или источник - http сервер. Если layout.cache - список нужных ключей будет послан как POST запрос -->
|
||
<http>
|
||
<url>http://[::1]/os.tsv</url>
|
||
<!-- С помощью какого формата понимать ответ и формировать список ключей. -->
|
||
<format>TabSeparated</format>
|
||
</http>
|
||
|
||
</source>
|
||
|
||
<!-- Периодичность обновления для полностью загружаемых словарей. 0 - никогда не обновлять. -->
|
||
<lifetime>
|
||
<min>300</min>
|
||
<max>360</max>
|
||
<!-- Периодичность обновления выбирается равномерно-случайно между min и max,
|
||
чтобы размазать по времени нагрузку при обновлении словарей на большом количестве серверов. -->
|
||
</lifetime>
|
||
|
||
<!-- или
|
||
<!- - Периодичность обновления для полностью загружаемых словарей или время инвалидации для кэшируемых словарей.
|
||
0 - никогда не обновлять. - ->
|
||
<lifetime>300</lifetime>
|
||
-->
|
||
|
||
<layout> <!-- Способ размещения в памяти. -->
|
||
<flat />
|
||
<!-- или
|
||
<hashed />
|
||
или
|
||
<cache>
|
||
<!- - Размер кэша в количестве ячеек; округляется вверх до степени двух. - ->
|
||
<size_in_cells>1000000000</size_in_cells>
|
||
</cache>
|
||
-->
|
||
</layout>
|
||
|
||
<!-- Структура. -->
|
||
<structure>
|
||
<!-- Описание столбца, являющегося идентификатором (ключом) словаря. -->
|
||
<id>
|
||
<!-- Имя столбца с идентификатором. -->
|
||
<name>Id</name>
|
||
</id>
|
||
|
||
<attribute> <!-- id уже входит в атрибуты и дополнительно указывать его здесь не нужно. -->
|
||
<!-- Имя столбца. -->
|
||
<name>Name</name>
|
||
<!-- Тип столбца. (Как столбец понимается при загрузке.
|
||
В случае MySQL, в таблице может быть TEXT, VARCHAR, BLOB, но загружается всё как String) -->
|
||
<type>String</type>
|
||
<!-- Какое значение использовать для несуществующего элемента. В примере - пустая строка. -->
|
||
<null_value></null_value>
|
||
</attribute>
|
||
|
||
<!-- Может быть указано произвольное количество атрибутов. -->
|
||
<attribute>
|
||
<name>ParentID</name>
|
||
<type>UInt64</type>
|
||
<null_value>0</null_value>
|
||
<!-- Определяет ли иерархию - отображение в идентификатор родителя (по умолчанию, false). -->
|
||
<hierarchical>true</hierarchical>
|
||
<!-- Можно считать отображение id -> attribute инъективным, чтобы оптимизировать GROUP BY. (по умолчанию, false) -->
|
||
<injective>true</injective>
|
||
</attribute>
|
||
|
||
<!-- Атрибут может быть выражением -->
|
||
<attribute>
|
||
<name>expr</name>
|
||
<type>UInt64</type>
|
||
<expression>rand64()</expression>
|
||
<null_value>0</null_value>
|
||
</attribute>
|
||
</structure>
|
||
</dictionary>
|
||
</dictionaries>
|
||
|
||
Идентификатор (ключевой атрибут) словаря должен быть числом, помещающимся в UInt64.
|
||
Также есть возможность задавать произвольные составные ключи (см. раздел "Словари с составными ключами"). Замечание: составной ключ может состоять и из одного элемента, что даёт возможность использовать в качестве ключа, например, строку.
|
||
|
||
|
||
Существует шесть способов размещения словаря в памяти.
|
||
|
||
flat
|
||
-----
|
||
В виде плоских массивов. Самый эффективный способ. Он подходит, если все ключи меньше 500 000. Если при создании словаря обнаружен ключ больше, то кидается исключение и словарь не создаётся. Словарь загружается в оперативку целиком. Словарь использует количество оперативки, пропорциональное максимальному значению ключа. Ввиду ограничения на 500 000, потребление оперативки вряд ли может быть большим.
|
||
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
|
||
|
||
hashed
|
||
------
|
||
В виде хэш-таблиц. Слегка менее эффективный способ. Словарь тоже загружается в оперативку целиком, и может содержать произвольное количество элементов с произвольными идентификаторами. На практике, имеет смысл использовать до десятков миллионов элементов, пока хватает оперативки.
|
||
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
|
||
|
||
cache
|
||
-----
|
||
Наименее эффективный способ. Подходит, если словарь не помещается в оперативку. Представляет собой кэш из фиксированного количества ячеек, в которых могут быть расположены часто используемые данные. Поддерживается источник MySQL, ClickHouse, executable, http; источник-файл не поддерживается. При поиске в словаре, сначала просматривается кэш. На каждый блок данных, все не найденные в кэше ключи (или устаревшие ключи) собираются в пачку, и с этой пачкой делается запрос к источнику вида SELECT attrs... FROM db.table WHERE id IN (k1, k2, ...). Затем полученные данные записываются в кэш.
|
||
|
||
range_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 |
|
||
+------------------+-----------------------------+------------+----------+
|
||
|
||
Добавляем ``layout = range_hashed``.
|
||
При использовании такого layout, в structure должны быть элементы ``range_min``, ``range_max``.
|
||
|
||
Пример:
|
||
|
||
.. code-block:: xml
|
||
|
||
<structure>
|
||
<id>
|
||
<name>Id</name>
|
||
</id>
|
||
<range_min>
|
||
<name>first</name>
|
||
</range_min>
|
||
<range_max>
|
||
<name>last</name>
|
||
</range_max>
|
||
...
|
||
|
||
Эти столбцы должны иметь тип Date. Другие типы пока не поддерживаем.
|
||
Столбцы обозначают закрытый диапазон дат.
|
||
|
||
Для работы с такими словарями, функции dictGetT должны принимать ещё один аргумент - дату:
|
||
|
||
``dictGetT('dict_name', 'attr_name', id, date)``
|
||
|
||
Функция достаёт значение для данного id и для диапазона дат, в который входит переданная дата. Если не найден id или для найденного id не найден диапазон, то возвращается значение по умолчанию для словаря.
|
||
|
||
Если есть перекрывающиеся диапазоны, то можно использовать любой подходящий.
|
||
|
||
Если граница диапазона является NULL или является некорректной датой (1900-01-01, 2039-01-01), то диапазон следует считать открытым. Диапазон может быть открытым с обеих сторон.
|
||
|
||
В оперативке данные представлены в виде хэш-таблицы со значением в виде упорядоченного массива диапазонов и соответствующих им значений.
|
||
|
||
Пример словаря по диапазонам:
|
||
|
||
.. code-block:: xml
|
||
|
||
<dictionaries>
|
||
<dictionary>
|
||
<name>xxx</name>
|
||
<source>
|
||
<mysql>
|
||
<password>xxx</password>
|
||
<port>3306</port>
|
||
<user>xxx</user>
|
||
<replica>
|
||
<host>xxx</host>
|
||
<priority>1</priority>
|
||
</replica>
|
||
<db>dicts</db>
|
||
<table>xxx</table>
|
||
</mysql>
|
||
</source>
|
||
<lifetime>
|
||
<min>300</min>
|
||
<max>360</max>
|
||
</lifetime>
|
||
<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>
|
||
</dictionaries>
|
||
|
||
complex_key_hashed
|
||
------------------
|
||
|
||
Для использования с составными ключами. Аналогичен hashed.
|
||
|
||
complex_key_cache
|
||
-----------------
|
||
|
||
Для использования с составными ключами. Аналогичен cache.
|
||
|
||
Примечания
|
||
----------
|
||
|
||
Рекомендуется использовать способ ``flat``, если возможно, или ``hashed``, ``complex_key_hashed``. Скорость работы словарей с таким размещением в памяти является безупречной.
|
||
|
||
Способы ``cache`` и ``complex_key_cache`` следует использовать лишь если это неизбежно. Скорость работы кэша очень сильно зависит от правильности настройки и сценария использования. Словарь типа cache нормально работает лишь при достаточно больших hit rate-ах (рекомендуется 99% и выше). Посмотреть средний hit rate можно в таблице system.dictionaries. Укажите достаточно большой размер кэша. Количество ячеек следует подобрать экспериментальным путём - выставить некоторое значение, с помощью запроса добиться полной заполненности кэша, посмотреть на потребление оперативки (эта информация находится в таблице system.dictionaries); затем пропорционально увеличить количество ячеек так, чтобы расходовалось разумное количество оперативки. В качестве источника для кэша рекомендуется MySQL, MongoDB, так как ClickHouse плохо обрабатывает запросы со случайными чтениями.
|
||
|
||
Во всех случаях, производительность будет выше, если вызывать функцию для работы со словарём после ``GROUP BY``, или если доставаемый атрибут помечен как инъективный. Для cache словарей, производительность будет лучше, если вызывать функцию после LIMIT-а - для этого можно использовать подзапрос с LIMIT-ом, и снаружи вызывать функцию со словарём.
|
||
|
||
Атрибут называется инъективным, если разным ключам соответствуют разные значения атрибута. Тогда при использовании в ``GROUP BY`` функции, достающей значение атрибута по ключу, эта функция автоматически выносится из GROUP BY.
|
||
|
||
При обновлении словарей из файла, сначала проверяется время модификации файла, и загрузка производится только если файл изменился.
|
||
При обновлении из MySQL, для flat и hashed словарей, сначала делается запрос ``SHOW TABLE STATUS`` и смотрится время обновления таблицы. И если оно не NULL, то оно сравнивается с запомненным временем. Это работает для MyISAM таблиц, а для InnoDB таблиц время обновления неизвестно, поэтому загрузка из InnoDB делается при каждом обновлении.
|
||
|
||
Для cache-словарей может быть задано время устаревания (``lifetime``) данных в кэше. Если от загрузки данных в ячейке прошло больше времени, чем lifetime, то значение не используется, и будет запрошено заново при следующей необходимости его использовать.
|
||
|
||
Если словарь не удалось ни разу загрузить, то при попытке его использования, будет брошено исключение.
|
||
Если при запросе к источнику cached словаря возникла ошибка, то будет брошено исключение.
|
||
Обновление словарей (кроме загрузки при первом использовании) не блокирует запросы - во время обновления используется старая версия словаря. Если при обновлении возникнет ошибка, то ошибка пишется в лог сервера, а запросы продолжат использовать старую версию словарей.
|
||
|
||
Список внешних словарей и их статус можно посмотреть в таблице ``system.dictionaries``.
|
||
|
||
Для использования внешних словарей, смотрите раздел "Функции для работы с внешними словарями".
|
||
|
||
Обратите внимание, что вы можете преобразовать значения по небольшому словарю, указав всё содержимое словаря прямо в запросе SELECT - смотрите раздел "Функция transform". Эта функциональность никак не связана с внешними словарями.
|
||
|
||
Словари с составными ключами
|
||
----------------------------
|
||
|
||
В качестве ключа может выступать кортеж (tuple) из полей произвольных типов. Параметр layout в этом случае должен быть равен complex_key_hashed или complex_key_cache.
|
||
|
||
Структура ключа задаётся не в элементе ``<id>``, а в элементе ``<key>``. Поля ключа задаются в том же формате, что и атрибуты словаря. Пример:
|
||
|
||
.. code-block:: xml
|
||
|
||
<structure>
|
||
<key>
|
||
<attribute>
|
||
<name>field1</name>
|
||
<type>String</type>
|
||
</attribute>
|
||
<attribute>
|
||
<name>field2</name>
|
||
<type>UInt32</type>
|
||
</attribute>
|
||
...
|
||
</key>
|
||
...
|
||
|
||
|
||
При использовании такого словаря, в функции dictGet* в качестве ключа передаётся Tuple со значениями полей. Пример: ``dictGetString('dict_name', 'attr_name', tuple('field1', 123))``.
|