mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-24 02:30:51 +00:00
286 lines
23 KiB
ReStructuredText
286 lines
23 KiB
ReStructuredText
|
Внешние словари
|
|||
|
===============
|
|||
|
|
|||
|
Существует возможность подключать свои собственные словари из различных источников данных.
|
|||
|
Источником данных для словаря может быть файл на локальной файловой системе, сервер 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 и прописать её. - ->
|
|||
|
-->
|
|||
|
|
|||
|
<!-- или источник - исполняемый файл. Если 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>
|
|||
|
</structure>
|
|||
|
</dictionary>
|
|||
|
</dictionaries>
|
|||
|
|
|||
|
Идентификатор (ключевой атрибут) словаря должен быть числом, помещающимся в UInt64.
|
|||
|
Составные и строковые ключи не поддерживаются. Впрочем, если в вашем словаре сложный ключ, вы можете захэшировать его и использовать хэш в качестве ключа. Для этого можно использовать View (как в ClickHouse, так и в MySQL).
|
|||
|
|
|||
|
Существует шесть способов размещения словаря в памяти.
|
|||
|
|
|||
|
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
|
|||
|
----------------
|
|||
|
|
|||
|
complex_key_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". Эта функциональность никак не связана с внешними словарями.
|