ClickHouse/docs/ru/dicts/external_dicts.rst

347 lines
26 KiB
ReStructuredText
Raw Normal View History

Внешние словари
===============
Существует возможность подключать свои собственные словари из различных источников данных.
Источником данных для словаря может быть файл на локальной файловой системе, сервер ClickHouse, сервер MySQL, MongoDB или любой ODBC источник.
Словарь может полностью храниться в оперативке и периодически обновляться, или быть частично закэшированным в оперативке и динамически подгружать отсутствующие значения.
Конфигурация внешних словарей находится в отдельном файле или файлах, указанных в конфигурационном параметре dictionaries_config.
Этот параметр содержит абсолютный или относительный путь к файлу с конфигурацией словарей. Относительный путь - относительно директории с конфигурационным файлом сервера. Путь может содержать wildcard-ы \* и ? - тогда рассматриваются все подходящие файлы. Пример: dictionaries/\*.xml.
Конфигурация словарей, а также множество файлов с конфигурацией, может обновляться без перезапуска сервера. Сервер проверяет обновления каждые 5 секунд. То есть, словари могут подключаться динамически.
Создание словарей может производиться при старте сервера или при первом использовании. Это определяется конфигурационном параметром 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
CLICKHOUSE-2720: progress on website (#865) * update presentations * CLICKHOUSE-2936: redirect from clickhouse.yandex.ru and clickhouse.yandex.com * update submodule * lost files * CLICKHOUSE-2981: prefer sphinx docs over original reference * CLICKHOUSE-2981: docs styles more similar to main website + add flags to switch language links * update presentations * Less confusing directory structure (docs -> doc/reference/) * Minify sphinx docs too * Website release script: fail fast + pass docker hash on deploy * Do not underline links in docs * shorter * cleanup docker images * tune nginx config * CLICKHOUSE-3043: get rid of habrastorage links * Lost translation * CLICKHOUSE-2936: temporary client-side redirect * behaves weird in test * put redirect back * CLICKHOUSE-3047: copy docs txts to public too * move to proper file * remove old pages to avoid confusion * Remove reference redirect warning for now * Refresh README.md * Yellow buttons in docs * Use svg flags instead of unicode ones in docs * fix test website instance * Put flags to separate files * wrong flag * Copy Yandex.Metrica introduction from main page to docs * Yet another home page structure change, couple new blocks (CLICKHOUSE-3045) * Update Contacts section * CLICKHOUSE-2849: more detailed legal information * CLICKHOUSE-2978 preparation - split by files * More changes in Contacts block * Tune texts on index page * update presentations * One more benchmark * Add usage sections to index page, adapted from slides * Get the roadmap started, based on slides from last ClickHouse Meetup * CLICKHOUSE-2977: some rendering tuning * Get rid of excessive section in the end of getting started * Make headers linkable * CLICKHOUSE-2981: links to editing reference - https://github.com/yandex/ClickHouse/issues/849 * CLICKHOUSE-2981: fix mobile styles in docs * Ban crawling of duplicating docs * Open some external links in new tab * Ban old docs too * Lots of trivial fixes in english docs * Lots of trivial fixes in russian docs * Remove getting started copies in markdown * Add Yandex.Webmaster * Fix some sphinx warnings * More warnings fixed in english docs * More sphinx warnings fixed * Add code-block:: text * More code-block:: text * These headers look not that well * Better switch between documentation languages * merge use_case.rst into ya_metrika_task.rst * Edit the agg_functions.rst texts * Add lost empty lines
2017-06-13 04:15:47 +00:00
----
В виде плоских массивов. Самый эффективный способ. Он подходит, если все ключи меньше 500 000. Если при создании словаря обнаружен ключ больше, то кидается исключение и словарь не создаётся. Словарь загружается в оперативку целиком. Словарь использует количество оперативки, пропорциональное максимальному значению ключа. Ввиду ограничения на 500 000, потребление оперативки вряд ли может быть большим.
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
hashed
------
В виде хэш-таблиц. Слегка менее эффективный способ. Словарь тоже загружается в оперативку целиком, и может содержать произвольное количество элементов с произвольными идентификаторами. На практике, имеет смысл использовать до десятков миллионов элементов, пока хватает оперативки.
Поддерживаются все виды источников. При обновлении, данные (из файла, из таблицы) читаются целиком.
cache
-----
Наименее эффективный способ. Подходит, если словарь не помещается в оперативку. Представляет собой кэш из фиксированного количества ячеек, в которых могут быть расположены часто используемые данные. Поддерживается источник MySQL, ClickHouse, executable, http; источник-файл не поддерживается. При поиске в словаре, сначала просматривается кэш. На каждый блок данных, все не найденные в кэше ключи (или устаревшие ключи) собираются в пачку, и с этой пачкой делается запрос к источнику вида SELECT attrs... FROM db.table WHERE id IN (k1, k2, ...). Затем полученные данные записываются в кэш.
range_hashed
------------
В таблице прописаны какие-то данные для диапазонов дат, для каждого ключа. Дать возможность доставать эти данные для заданного ключа, для заданной даты.
Пример: таблица содержит скидки для каждого рекламодателя в виде:
CLICKHOUSE-2720: progress on website and reference (#886) * update presentations * CLICKHOUSE-2936: redirect from clickhouse.yandex.ru and clickhouse.yandex.com * update submodule * lost files * CLICKHOUSE-2981: prefer sphinx docs over original reference * CLICKHOUSE-2981: docs styles more similar to main website + add flags to switch language links * update presentations * Less confusing directory structure (docs -> doc/reference/) * Minify sphinx docs too * Website release script: fail fast + pass docker hash on deploy * Do not underline links in docs * shorter * cleanup docker images * tune nginx config * CLICKHOUSE-3043: get rid of habrastorage links * Lost translation * CLICKHOUSE-2936: temporary client-side redirect * behaves weird in test * put redirect back * CLICKHOUSE-3047: copy docs txts to public too * move to proper file * remove old pages to avoid confusion * Remove reference redirect warning for now * Refresh README.md * Yellow buttons in docs * Use svg flags instead of unicode ones in docs * fix test website instance * Put flags to separate files * wrong flag * Copy Yandex.Metrica introduction from main page to docs * Yet another home page structure change, couple new blocks (CLICKHOUSE-3045) * Update Contacts section * CLICKHOUSE-2849: more detailed legal information * CLICKHOUSE-2978 preparation - split by files * More changes in Contacts block * Tune texts on index page * update presentations * One more benchmark * Add usage sections to index page, adapted from slides * Get the roadmap started, based on slides from last ClickHouse Meetup * CLICKHOUSE-2977: some rendering tuning * Get rid of excessive section in the end of getting started * Make headers linkable * CLICKHOUSE-2981: links to editing reference - https://github.com/yandex/ClickHouse/issues/849 * CLICKHOUSE-2981: fix mobile styles in docs * Ban crawling of duplicating docs * Open some external links in new tab * Ban old docs too * Lots of trivial fixes in english docs * Lots of trivial fixes in russian docs * Remove getting started copies in markdown * Add Yandex.Webmaster * Fix some sphinx warnings * More warnings fixed in english docs * More sphinx warnings fixed * Add code-block:: text * More code-block:: text * These headers look not that well * Better switch between documentation languages * merge use_case.rst into ya_metrika_task.rst * Edit the agg_functions.rst texts * Add lost empty lines * Lost blank lines * Add new logo sizes * update presentations * Next step in migrating to new documentation * Fix all warnings in en reference * Fix all warnings in ru reference * Re-arrange existing reference * Move operation tips to main reference * Fix typos noticed by milovidov@ * Get rid of zookeeper.md * Looks like duplicate of tutorial.html * Fix some mess with html tags in tutorial * No idea why nobody noticed this before, but it was completely not clear whet to get the data * Match code block styling between main and tutorial pages (in favor of the latter) * Get rid of some copypaste in tutorial * Normalize header styles * Move example_datasets to sphinx * Move presentations submodule to website * Move and update README.md * No point in duplicating articles from habrahabr here * Move development-related docs as is for now * doc/reference/ -> docs/ (to match the URL on website) * Adapt links to match the previous commit * Adapt development docs to rst (still lacks translation and strikethrough support) * clean on release * blacklist presentations in gulp * strikethrough support in sphinx * just copy development folder for now * fix weird introduction in style article * Style guide translation (WIP) * Finish style guide translation to English * gulp clean separately * Update year in LICENSE * Initial CONTRIBUTING.md * Fix remaining links to old docs in tutorial * Some tutorial fixes * Typo * Another typo * Update list of authors from yandex-team accoding to git log
2017-06-20 14:19:03 +00:00
.. code-block:: text
CLICKHOUSE-2720: progress on website (#865) * update presentations * CLICKHOUSE-2936: redirect from clickhouse.yandex.ru and clickhouse.yandex.com * update submodule * lost files * CLICKHOUSE-2981: prefer sphinx docs over original reference * CLICKHOUSE-2981: docs styles more similar to main website + add flags to switch language links * update presentations * Less confusing directory structure (docs -> doc/reference/) * Minify sphinx docs too * Website release script: fail fast + pass docker hash on deploy * Do not underline links in docs * shorter * cleanup docker images * tune nginx config * CLICKHOUSE-3043: get rid of habrastorage links * Lost translation * CLICKHOUSE-2936: temporary client-side redirect * behaves weird in test * put redirect back * CLICKHOUSE-3047: copy docs txts to public too * move to proper file * remove old pages to avoid confusion * Remove reference redirect warning for now * Refresh README.md * Yellow buttons in docs * Use svg flags instead of unicode ones in docs * fix test website instance * Put flags to separate files * wrong flag * Copy Yandex.Metrica introduction from main page to docs * Yet another home page structure change, couple new blocks (CLICKHOUSE-3045) * Update Contacts section * CLICKHOUSE-2849: more detailed legal information * CLICKHOUSE-2978 preparation - split by files * More changes in Contacts block * Tune texts on index page * update presentations * One more benchmark * Add usage sections to index page, adapted from slides * Get the roadmap started, based on slides from last ClickHouse Meetup * CLICKHOUSE-2977: some rendering tuning * Get rid of excessive section in the end of getting started * Make headers linkable * CLICKHOUSE-2981: links to editing reference - https://github.com/yandex/ClickHouse/issues/849 * CLICKHOUSE-2981: fix mobile styles in docs * Ban crawling of duplicating docs * Open some external links in new tab * Ban old docs too * Lots of trivial fixes in english docs * Lots of trivial fixes in russian docs * Remove getting started copies in markdown * Add Yandex.Webmaster * Fix some sphinx warnings * More warnings fixed in english docs * More sphinx warnings fixed * Add code-block:: text * More code-block:: text * These headers look not that well * Better switch between documentation languages * merge use_case.rst into ya_metrika_task.rst * Edit the agg_functions.rst texts * Add lost empty lines
2017-06-13 04:15:47 +00:00
.. code-block:: text
+------------------+-----------------------------+------------+----------+
| 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))``.