ClickHouse/docs/ja/sql-reference/dictionaries/index.md
2024-11-18 11:58:58 +09:00

103 KiB
Raw Blame History

slug sidebar_label sidebar_position
/ja/sql-reference/dictionaries ディクショナリ定義 35

import SelfManaged from '@site/docs/ja/_snippets/_self_managed_only_no_roadmap.md'; import CloudDetails from '@site/docs/ja/sql-reference/dictionaries/_snippet_dictionary_in_cloud.md';

ディクショナリ

ディクショナリは、さまざまな種類の参照リストに便利なマッピング (key -> attributes) です。

ClickHouseはクエリで使用できるディクショナリを操作するための特別な関数をサポートしています。リファレンステーブルとの JOIN よりも、関数を使用してディクショナリを使うほうが簡単かつ効率的です。

ClickHouseは次をサポートしています:

:::tip チュートリアル ClickHouseでディクショナリを始める場合には、そのトピックをカバーしたチュートリアルがあります。こちらをご覧ください。 :::

クリックハウスのディクショナリはさまざまなデータソースから追加できます。ディクショナリのソースには、ClickHouseテーブル、ローカルテキストまたは実行ファイル、HTTP(s)リソース、または他のDBMSを使うことができます。詳細は「ディクショナリソース」を参照してください。

ClickHouse:

  • ディクショナリをRAMに完全または部分的に保存します。
  • ディクショナリを定期的に更新し、欠けている値を動的にロードします。つまり、ディクショナリは動的にロードできます。
  • xmlファイルまたはDDLクエリでディクショナリを作成できます。

ディクショナリの設定は、1つ以上のxmlファイルに配置できます。設定へのパスは、dictionaries_configパラメータで指定します。

ディクショナリは、サーバーの起動時または最初の使用時にロードされますが、それはdictionaries_lazy_load設定に依存します。

システムテーブルdictionariesには、サーバーで設定されたディクショナリの情報が含まれています。各ディクショナリについて、次の情報を確認できます:

  • ディクショナリのステータス。
  • 設定パラメータ。
  • ディクショナリが正常にロードされてからのRAM使用量やクエリ数などのメトリック。

DDLクエリを使用したディクショナリの作成

ディクショナリはDDLクエリを使用して作成できます。DDLで作成したディクショナリは次の利点があるため、推奨される方法です:

  • サーバーの設定ファイルに追加のレコードが追加されない
  • ディクショナリはテーブルやビューのように第一級のエンティティとして扱える
  • ディクショナリテーブル関数ではなく、SELECTを使用して直接データを読み取ることができる
  • ディクショナリの名前を簡単に変更できる

設定ファイルを使用したディクショナリの作成

:::note 設定ファイルを用いたディクショナリの作成は、ClickHouse Cloudには適用されません。上記のDDLを使用し、ユーザーdefaultとしてディクショナリを作成してください。 :::

ディクショナリの設定ファイルは次の形式を持ちます:

<clickhouse>
    <comment>任意のコンテンツを持つオプション要素。ClickHouseサーバーによって無視されます。</comment>

    <!--オプション要素。置換が含まれたファイル名-->
    <include_from>/etc/metrika.xml</include_from>

    <dictionary>
        <!-- ディクショナリ設定。 -->
        <!-- 設定ファイルには任意の数のディクショナリセクションを含むことができます。 -->
    </dictionary>

</clickhouse>

同じファイルで任意の数のディクショナリを設定できます。

:::note 小さなディクショナリの値をSELECTクエリで記述することで変換できます(transform関数を参照)。この機能はディクショナリとは関係ありません。 :::

ディクショナリの設定

ディクショナリがxmlファイルで設定されている場合、ディクショナリの設定は次の構造を持ちます:

<dictionary>
    <name>dict_name</name>

    <structure>
      <!-- 複合キーの設定 -->
    </structure>

    <source>
      <!-- ソースの設定 -->
    </source>

    <layout>
      <!-- メモリレイアウトの設定 -->
    </layout>

    <lifetime>
      <!-- メモリにおけるディクショナリの寿命 -->
    </lifetime>
</dictionary>

対応するDDLクエリは次の構造です:

CREATE DICTIONARY dict_name
(
    ... -- 属性
)
PRIMARY KEY ... -- 複合または単一キーの設定
SOURCE(...) -- ソース設定
LAYOUT(...) -- メモリレイアウトの設定
LIFETIME(...) -- メモリにおけるディクショナリの寿命

メモリにディクショナリを保存する方法

ディクショナリをメモリに保存する方法はさまざまです。

最適な処理速度を提供するflathashedおよびcomplex_key_hashedをお勧めします。

キャッシュは潜在的な低パフォーマンスと最適なパラメータの選択が難しいため推奨されません。cacheセクションで詳細を読むことができます。

ディクショナリのパフォーマンスを向上させるいくつかの方法があります:

  • ディクショナリで操作するための関数をGROUP BYの後に呼び出す。
  • 抽出する属性を全射的であるとマークします。属性が異なるキーに対応する場合、それを全射と呼びます。つまり、GROUP BYがキーで属性値を取得する関数を使用する場合、この関数がGROUP BYから自動的に取り出されます。

ClickHouseはエラーがディクショナリで発生した場合に例外を生成します。エラーの例:

  • アクセスしようとしたディクショナリがロードできなかった。
  • cachedディクショナリをクエリする際のエラー。

system.dictionariesテーブルでディクショナリのリストとそのステータスを表示できます。

設定は次のようになります:

<clickhouse>
    <dictionary>
        ...
        <layout>
            <layout_type>
                <!-- レイアウト設定 -->
            </layout_type>
        </layout>
        ...
    </dictionary>
</clickhouse>

対応するDDLクエリは次の通りです:

CREATE DICTIONARY (...)
...
LAYOUT(LAYOUT_TYPE(param value)) -- レイアウト設定
...

レイアウトにcomplex-key*という言葉が含まれていないディクショナリは、UInt64型のキーを持ち、complex-key*ディクショナリは複合キー(任意の型)を持ちます。

XMLディクショナリ内のUInt64キーは<id>タグで定義します。

設定の例カラムkey_columnがUInt64型の場合:

...
<structure>
    <id>
        <name>key_column</name>
    </id>
...

複合complexキーのXMLディクショナリは<key>タグで定義します。

複合キーの設定例(キーがString型の1要素を持つ場合:

...
<structure>
    <key>
        <attribute>
            <name>country_code</name>
            <type>String</type>
        </attribute>
    </key>
...

メモリにディクショナリを保存する方法

flat

ディクショナリはフラット配列の形でメモリに完全に保存されます。ディクショナリのメモリ使用量はどれくらいですか?それは最大サイズのキーに比例します。

ディクショナリキーはUInt64型を持ち、値はmax_array_sizeデフォルトは500,000に制限されます。ディクショナリ作成時により大きなキーが見つかった場合、ClickHouseは例外をスローし、ディクショナリを作成しません。ディクショナリのフラット配列初期サイズはinitial_array_size設定デフォルトは1024で制御されます。

全ての種類のソースがサポートされています。更新する際には、データ(ファイルまたはテーブルから)は全体で読み込まれます。

この方法は、ディクショナリを保存するためのすべての方法の中で最高のパフォーマンスを提供します。

設定の例:

<layout>
  <flat>
    <initial_array_size>50000</initial_array_size>
    <max_array_size>5000000</max_array_size>
  </flat>
</layout>

または

LAYOUT(FLAT(INITIAL_ARRAY_SIZE 50000 MAX_ARRAY_SIZE 5000000))

hashed

ディクショナリは、ハッシュテーブルの形でメモリに完全に保存されます。ディクショナリは任意の数の要素と識別子を持つことができます。実際には、キーの数は何千万アイテムに達する可能性があります。

ディクショナリキーはUInt64型を持ちます。

全ての種類のソースがサポートされています。更新する際には、データ(ファイルまたはテーブルから)が全体として読み込まれます。

設定の例:

<layout>
  <hashed />
</layout>

または

LAYOUT(HASHED())

設定の例:

<layout>
  <hashed>
    <!-- シャードの数が1を超えるデフォルトは`1`)場合、ディクショナリは並列でデータをロードします。
         1つのディクショナリに大量の要素がある場合に便利です。 -->
    <shards>10</shards>

    <!-- 並列キュー内のブロックのバックログのサイズ。
         並列ロードのボトルネックは再ハッシュであり、そのためスレッドが再ハッシュを実行するために停止しないようにするために
         ある程度のバックログが必要です。
         メモリと速度のバランスでは10000が良いです。
         10e10要素でも全負荷を処理できます。 -->
    <shard_load_queue_backlog>10000</shard_load_queue_backlog>

    <!-- ハッシュテーブルの最大ロードファクタ。高い値では、メモリがより効率的に使用され(メモリが無駄にならない)ますが、
         読み取り/パフォーマンスが低下する可能性があります。
         有効な値: [0.5, 0.99]
         デフォルト: 0.5 -->
    <max_load_factor>0.5</max_load_factor>
  </hashed>
</layout>

または

LAYOUT(HASHED([SHARDS 1] [SHARD_LOAD_QUEUE_BACKLOG 10000] [MAX_LOAD_FACTOR 0.5]))

sparse_hashed

hashed に似ていますが、より多くのCPU使用量に対してメモリを少なく使用します。

ディクショナリキーはUInt64型を持ちます。

設定の例:

<layout>
  <sparse_hashed>
    <!-- <shards>1</shards> -->
    <!-- <shard_load_queue_backlog>10000</shard_load_queue_backlog> -->
    <!-- <max_load_factor>0.5</max_load_factor> -->
  </sparse_hashed>
</layout>

または

LAYOUT(SPARSE_HASHED([SHARDS 1] [SHARD_LOAD_QUEUE_BACKLOG 10000] [MAX_LOAD_FACTOR 0.5]))

この種類のディクショナリでは、shardsを使用することも可能で、再びそれはhashedに比べてsparse_hashedの方がより重要です。なぜなら、sparse_hashedはより遅いためです。

complex_key_hashed

このストレージタイプは、複合キーと共に使用されます。hashedに似ています。

設定の例:

<layout>
  <complex_key_hashed>
    <!-- <shards>1</shards> -->
    <!-- <shard_load_queue_backlog>10000</shard_load_queue_backlog> -->
    <!-- <max_load_factor>0.5</max_load_factor> -->
  </complex_key_hashed>
</layout>

または

LAYOUT(COMPLEX_KEY_HASHED([SHARDS 1] [SHARD_LOAD_QUEUE_BACKLOG 10000] [MAX_LOAD_FACTOR 0.5]))

complex_key_sparse_hashed

このストレージタイプは、複合キーと共に使用されます。sparse_hashedに似ています。

設定の例:

<layout>
  <complex_key_sparse_hashed>
    <!-- <shards>1</shards> -->
    <!-- <shard_load_queue_backlog>10000</shard_load_queue_backlog> -->
    <!-- <max_load_factor>0.5</max_load_factor> -->
  </complex_key_sparse_hashed>
</layout>

または

LAYOUT(COMPLEX_KEY_SPARSE_HASHED([SHARDS 1] [SHARD_LOAD_QUEUE_BACKLOG 10000] [MAX_LOAD_FACTOR 0.5]))

hashed_array

ディクショナリは完全にメモリに保存されます。各属性は配列に保存されます。キー属性はハッシュテーブルの形式で保存され、値は属性配列のインデックスです。ディクショナリは任意の数の要素と識別子を持つことができます。実際には、キーの数は何千万アイテムに達する可能性があります。

ディクショナリキーはUInt64型を持ちます。

全ての種類のソースがサポートされています。更新する際には、データ(ファイルまたはテーブルから)が全体として読み込まれます。

設定の例:

<layout>
  <hashed_array>
  </hashed_array>
</layout>

または

LAYOUT(HASHED_ARRAY([SHARDS 1]))

complex_key_hashed_array

このストレージタイプは、複合キーと共に使用されます。hashed_arrayに似ています。

設定の例:

<layout>
  <complex_key_hashed_array />
</layout>

または

LAYOUT(COMPLEX_KEY_HASHED_ARRAY([SHARDS 1]))

range_hashed

ディクショナリは、範囲とそれに対応する値の順序付き配列を持つハッシュテーブルの形でメモリに保存されます。

ディクショナリキーはUInt64型を持ちます。 このストレージ方法は、hashedと同様に機能し、キーに加えて日付/時間(任意の数値型)の範囲を使用できます。

例: テーブルは各広告主に対する割引を次の形式で含みます:

┌─advertiser_id─┬─discount_start_date─┬─discount_end_date─┬─amount─┐
│           123 │          2015-01-16 │        2015-01-31 │   0.25 │
│           123 │          2015-01-01 │        2015-01-15 │   0.15 │
│           456 │          2015-01-01 │        2015-01-15 │   0.05 │
└───────────────┴─────────────────────┴───────────────────┴────────┘

日付範囲のサンプルを使用するには、structurerange_minrange_max要素を定義します。これらの要素はnametypetypeが指定されていない場合はデフォルト型であるDateが使用されますの要素を持つ必要があります。typeは任意の数値型Date / DateTime / UInt64 / Int32 / その他)が可能です。

:::note range_minrange_maxの値はInt64型に収まるべきです。 :::

例:

<layout>
    <range_hashed>
        <!-- 範囲の重複防止戦略min/max。デフォルト: min (範囲の値範囲で最小のminを持つ範囲を返す) -->
        <range_lookup_strategy>min</range_lookup_strategy>
    </range_hashed>
</layout>
<structure>
    <id>
        <name>advertiser_id</name>
    </id>
    <range_min>
        <name>discount_start_date</name>
        <type>Date</type>
    </range_min>
    <range_max>
        <name>discount_end_date</name>
        <type>Date</type>
    </range_max>
    ...

または

CREATE DICTIONARY discounts_dict (
    advertiser_id UInt64,
    discount_start_date Date,
    discount_end_date Date,
    amount Float64
)
PRIMARY KEY id
SOURCE(CLICKHOUSE(TABLE 'discounts'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(RANGE_HASHED(range_lookup_strategy 'max'))
RANGE(MIN discount_start_date MAX discount_end_date)

これらのディクショナリを操作するには、dictGet関数に範囲を選択するための追加の引数を渡す必要があります:

dictGet('dict_name', 'attr_name', id, date)

クエリ例:

SELECT dictGet('discounts_dict', 'amount', 1, '2022-10-20'::Date);

この関数は、指定されたidと、渡された日付を含む日付範囲の値を返します。

アルゴリズムの詳細:

  • idが見つからないか、idのための範囲が見つからない場合、その属性の型のデフォルト値を返します。
  • 重複する範囲があり、range_lookup_strategy=minである場合、最小のrange_minを持つ一致する範囲を返します。複数の範囲が見つかった場合、最小のrange_maxを持つ範囲を返します。それでも複数の範囲が(同じrange_minrange_maxを持つ複数の範囲があった場合)は、ランダムにその中の一つが返されます。
  • 重複する範囲があり、range_lookup_strategy=maxである場合、最大のrange_minを持つ一致する範囲を返します。複数の範囲が見つかった場合、最大のrange_maxを持つ範囲を返します。それでも複数の範囲が(同じrange_minrange_maxを持つ複数の範囲があった場合)は、ランダムにその中の一つが返されます。
  • range_maxNULLの場合、範囲は開かれています。NULLは最大可能値として扱われます。range_minに対しては、1970-01-01または0-MAX_INTが開いた値として使用できます。

設定例:

<clickhouse>
    <dictionary>
        ...

        <layout>
            <range_hashed />
        </layout>

        <structure>
            <id>
                <name>Abcdef</name>
            </id>
            <range_min>
                <name>StartTimeStamp</name>
                <type>UInt64</type>
            </range_min>
            <range_max>
                <name>EndTimeStamp</name>
                <type>UInt64</type>
            </range_max>
            <attribute>
                <name>XXXType</name>
                <type>String</type>
                <null_value />
            </attribute>
        </structure>

    </dictionary>
</clickhouse>

または

CREATE DICTIONARY somedict(
    Abcdef UInt64,
    StartTimeStamp UInt64,
    EndTimeStamp UInt64,
    XXXType String DEFAULT ''
)
PRIMARY KEY Abcdef
RANGE(MIN StartTimeStamp MAX EndTimeStamp)

重複する範囲と開いた範囲の設定例:

CREATE TABLE discounts
(
    advertiser_id UInt64,
    discount_start_date Date,
    discount_end_date Nullable(Date),
    amount Float64
)
ENGINE = Memory;

INSERT INTO discounts VALUES (1, '2015-01-01', Null, 0.1);
INSERT INTO discounts VALUES (1, '2015-01-15', Null, 0.2);
INSERT INTO discounts VALUES (2, '2015-01-01', '2015-01-15', 0.3);
INSERT INTO discounts VALUES (2, '2015-01-04', '2015-01-10', 0.4);
INSERT INTO discounts VALUES (3, '1970-01-01', '2015-01-15', 0.5);
INSERT INTO discounts VALUES (3, '1970-01-01', '2015-01-10', 0.6);

SELECT * FROM discounts ORDER BY advertiser_id, discount_start_date;
┌─advertiser_id─┬─discount_start_date─┬─discount_end_date─┬─amount─┐
             1           2015-01-01               ᴺᵁᴸᴸ     0.1 
             1           2015-01-15               ᴺᵁᴸᴸ     0.2 
             2           2015-01-01         2015-01-15     0.3 
             2           2015-01-04         2015-01-10     0.4 
             3           1970-01-01         2015-01-15     0.5 
             3           1970-01-01         2015-01-10     0.6 
└───────────────┴─────────────────────┴───────────────────┴────────┘

-- RANGE_LOOKUP_STRATEGY 'max'

CREATE DICTIONARY discounts_dict
(
    advertiser_id UInt64,
    discount_start_date Date,
    discount_end_date Nullable(Date),
    amount Float64
)
PRIMARY KEY advertiser_id
SOURCE(CLICKHOUSE(TABLE discounts))
LIFETIME(MIN 600 MAX 900)
LAYOUT(RANGE_HASHED(RANGE_LOOKUP_STRATEGY 'max'))
RANGE(MIN discount_start_date MAX discount_end_date);

select dictGet('discounts_dict', 'amount', 1, toDate('2015-01-14')) res;
┌─res─┐
 0.1  -- 一致する範囲は1つのみ: 2015-01-01 - Null
└─────┘

select dictGet('discounts_dict', 'amount', 1, toDate('2015-01-16')) res;
┌─res─┐
 0.2  -- 2つの範囲が一致、range_min 2015-01-15 (0.2)が2015-01-01 (0.1)より大きい
└─────┘

select dictGet('discounts_dict', 'amount', 2, toDate('2015-01-06')) res;
┌─res─┐
 0.4  -- 2つの範囲が一致、range_min 2015-01-04 (0.4)が2015-01-01 (0.3)より大きい
└─────┘

select dictGet('discounts_dict', 'amount', 3, toDate('2015-01-01')) res;
┌─res─┐
 0.5  -- 2つの範囲が一致、range_minは等しい、2015-01-15 (0.5)が2015-01-10 (0.6)より大きい
└─────┘

DROP DICTIONARY discounts_dict;

-- RANGE_LOOKUP_STRATEGY 'min'

CREATE DICTIONARY discounts_dict
(
    advertiser_id UInt64,
    discount_start_date Date,
    discount_end_date Nullable(Date),
    amount Float64
)
PRIMARY KEY advertiser_id
SOURCE(CLICKHOUSE(TABLE discounts))
LIFETIME(MIN 600 MAX 900)
LAYOUT(RANGE_HASHED(RANGE_LOOKUP_STRATEGY 'min'))
RANGE(MIN discount_start_date MAX discount_end_date);

select dictGet('discounts_dict', 'amount', 1, toDate('2015-01-14')) res;
┌─res─┐
 0.1  -- 一致する範囲は1つだけ: 2015-01-01 - Null
└─────┘

select dictGet('discounts_dict', 'amount', 1, toDate('2015-01-16')) res;
┌─res─┐
 0.1  -- 2つの範囲が一致、range_min 2015-01-01 (0.1) が2015-01-15 (0.2)より小さい
└─────┘

select dictGet('discounts_dict', 'amount', 2, toDate('2015-01-06')) res;
┌─res─┐
 0.3  -- 2つの範囲が一致、range_min 2015-01-01 (0.3) が2015-01-04 (0.4)より小さい
└─────┘

select dictGet('discounts_dict', 'amount', 3, toDate('2015-01-01')) res;
┌─res─┐
 0.6  -- 2つの範囲が一致、range_minは等しい、2015-01-10 (0.6) が2015-01-15 (0.5)より小さい
└─────┘

complex_key_range_hashed

ディクショナリは、範囲とそれに対応する値の順序付き配列を持つハッシュテーブルの形でメモリに保存されます(range_hashedを参照)。このストレージタイプは複合キー用です。

設定例:

CREATE DICTIONARY range_dictionary
(
  CountryID UInt64,
  CountryKey String,
  StartDate Date,
  EndDate Date,
  Tax Float64 DEFAULT 0.2
)
PRIMARY KEY CountryID, CountryKey
SOURCE(CLICKHOUSE(TABLE 'date_table'))
LIFETIME(MIN 1 MAX 1000)
LAYOUT(COMPLEX_KEY_RANGE_HASHED())
RANGE(MIN StartDate MAX EndDate);

cache

ディクショナリは、固定数のセルを持つキャッシュに保存されます。これらのセルには頻繁に使用される要素が含まれます。

ディクショナリキーはUInt64型を持ちます。

ディクショナリを検索する際、まずキャッシュが検索されます。データブロックごとに、キャッシュに見つからない、または期限切れのすべてのキーがSELECT attrs... FROM db.table WHERE id IN (k1, k2, ...)を使ってソースから要求されます。受信したデータはキャッシュに書き込まれます。

キーがディクショナリに見つからない場合、キャッシュ更新タスクが作成され、更新キューに追加されます。更新キューの特性はmax_update_queue_sizeupdate_queue_push_timeout_millisecondsquery_wait_timeout_millisecondsmax_threads_for_updates設定によって制御できます。

キャッシュディクショナリの場合、lifetimeでデータの有効期限を設定できます。セルにデータがロードされてからlifetimeを超える時間が経過した場合、セルの値は使用されず、キーが期限切れになります。このキーは次回使用時に再要求されます。この動作はallow_read_expired_keys設定で構成できます。

これはディクショナリを保存するすべての方法の中で最も効果が低いものです。キャッシュの速度は、設定と使用シナリオに大きく依存します。キャッシュタイプのディクショナリは、ヒット率が十分に高い場合にのみ良好に動作します推奨99%以上)。system.dictionariesテーブルで平均ヒット率を確認できます。

設定allow_read_expired_keysが1に設定されている場合デフォルトは0、ディクショナリは非同期更新をサポートできます。クライアントがキーを要求し、すべてのキーがキャッシュに存在するが、一部のキーが期限切れになっている場合、ディクショナリはクライアントに期限切れのキーを返し、ソースから非同期に要求されます。

キャッシュの性能を向上させるには、LIMIT付きのサブクエリを使用し、ディクショナリと外部に関数を呼び出します。

すべての種類のソースがサポートされています。

設定例:

<layout>
    <cache>
        <!-- キャッシュのサイズ、セルの数単位。2の累乗に切り上げられます。 -->
        <size_in_cells>1000000000</size_in_cells>
        <!-- 有効期限切れのキーを読み込むことを許可します。 -->
        <allow_read_expired_keys>0</allow_read_expired_keys>
        <!-- 更新キューの最大サイズ。 -->
        <max_update_queue_size>100000</max_update_queue_size>
        <!-- キューへの更新タスク投入の最大タイムアウト時間(ミリ秒単位)。 -->
        <update_queue_push_timeout_milliseconds>10</update_queue_push_timeout_milliseconds>
        <!-- 更新タスク完了のための最大待機タイムアウト時間(ミリ秒単位)。 -->
        <query_wait_timeout_milliseconds>60000</query_wait_timeout_milliseconds>
        <!-- キャッシュディクショナリの最大更新スレッド数。 -->
        <max_threads_for_updates>4</max_threads_for_updates>
    </cache>
</layout>

または

LAYOUT(CACHE(SIZE_IN_CELLS 1000000000))

十分なキャッシュサイズを設定してください。セル数を選択するには実験が必要です:

  1. 各種値を設定します。
  2. キャッシュが完全に満たされるまでクエリを実行します。
  3. system.dictionariesテーブルを使用してメモリ消費量を評価します。
  4. 必要なメモリ消費量に達するまでセル数を増減します。

:::note ClickHouseをソースとして使用しないでください。ランダム読みによるクエリ処理が遅いためです。 :::

complex_key_cache

このストレージタイプは、複合キーと共に使用されます。cacheに似ています。

ssd_cache

s_cacheに似ていますが、データをSSDに保存しインデックスをRAMに保存します。更新キューに関連するすべてのキャッシュディクショナリ設定もSSDキャッシュディクショナリに適用できます。

ディクショナリキーはUInt64型を持ちます。

<layout>
    <ssd_cache>
        <!-- elementary read blockのサイズバイト単位             SSDのページサイズに等しくすることをお勧めします。 -->
        <block_size>4096</block_size>
        <!-- キャッシュファイルの最大サイズ(バイト単位)。 -->
        <file_size>16777216</file_size>
        <!-- SSDから要素を読み込むためのRAMバッファのサイズバイト単位。 -->
        <read_buffer_size>131072</read_buffer_size>
        <!-- SSDにフラッシュする前に要素を集約するためのRAMバッファのサイズバイト単位。 -->
        <write_buffer_size>1048576</write_buffer_size>
        <!-- キャッシュファイルが保存されるパス。 -->
        <path>/var/lib/clickhouse/user_files/test_dict</path>
    </ssd_cache>
</layout>

または

LAYOUT(SSD_CACHE(BLOCK_SIZE 4096 FILE_SIZE 16777216 READ_BUFFER_SIZE 1048576
    PATH '/var/lib/clickhouse/user_files/test_dict'))

complex_key_ssd_cache

このストレージタイプは、複合キーと共に使用されます。ssd_cacheに似ています。

direct

ディクショナリはメモリに保存されず、リクエストの処理中にソース に直接アクセスします。

ディクショナリキーはUInt64型を持ちます。

ローカルファイルを除くすべてのソースがサポートされます。

設定例:

<layout>
  <direct />
</layout>

または

LAYOUT(DIRECT())

complex_key_direct

このストレージタイプは、複合キーと共に使用されます。directに似ています。

ip_trie

このストレージタイプはネットワークプレフィックスIPアドレスをASNなどのメタデータにマッピングするためのものです。

ClickHouseにIPプレフィックスとマッピングが含まれるテーブルがあるとします:

CREATE TABLE my_ip_addresses (
	prefix String,
	asn UInt32,
	cca2 String
)
ENGINE = MergeTree
PRIMARY KEY prefix;
INSERT INTO my_ip_addresses VALUES
	('202.79.32.0/20', 17501, 'NP'),
    ('2620:0:870::/48', 3856, 'US'),
    ('2a02:6b8:1::/48', 13238, 'RU'),
    ('2001:db8::/32', 65536, 'ZZ')
;

このテーブルのip_trieディクショナリを定義しましょう。ip_trieレイアウトは複合キーを必要とします:

<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>
    ...
</structure>
<layout>
    <ip_trie>
        <!-- キー属性`prefix`はdictGetStringで取得できます。 -->
        <!-- このオプションはメモリ使用量を増やします。 -->
        <access_to_key_from_attributes>true</access_to_key_from_attributes>
    </ip_trie>
</layout>

または

CREATE DICTIONARY my_ip_trie_dictionary (
    prefix String,
    asn UInt32,
    cca2 String DEFAULT '??'
)
PRIMARY KEY prefix
SOURCE(CLICKHOUSE(TABLE 'my_ip_addresses'))
LAYOUT(IP_TRIE)
LIFETIME(3600);

キーは許可されたIPプレフィックスを含むString型属性を一つだけ持たなければなりません。他の型はまだサポートされていません。

構文は以下の通りです:

dictGetT('dict_name', 'attr_name', ip)

関数はUInt32IPv4の場合、またはFixedString(16)IPv6の場合を取ります。例 :

SELECT dictGet('my_ip_trie_dictionary', 'cca2', toIPv4('202.79.32.10')) AS result;

┌─result─┐
 NP     
└────────┘


SELECT dictGet('my_ip_trie_dictionary', 'asn', IPv6StringToNum('2001:db8::1')) AS result;

┌─result─┐
  65536 
└────────┘


SELECT dictGet('my_ip_trie_dictionary', ('asn', 'cca2'), IPv6StringToNum('2001:db8::1')) AS result;

┌─result───────┐
 (65536,'ZZ') 
└──────────────┘

他の型はまだサポートされていません。関数は、このIPアドレスに対応するプレフィックスの属性を返します。プレフィックスが重複する場合、最も特定のものが返されます。

データは完全にRAMに収まる必要があります。

LIFETIMEを使用したディクショナリデータの更新

ClickHouseは、LIFETIMEタグ(秒単位で定義)に基づいて定期的にディクショナリを更新します。LIFETIMEは完全にダウンロードされたディクショナリの更新間隔であり、キャッシュされたディクショナリの無効化間隔です。

更新の間、古いバージョンのDictionaryは依然としてクエリ可能です。Dictionaryの更新初めてDictionaryをロードする場合を除くはクエリをブロックしません。更新中にエラーが発生した場合、エラーはサーバーログに記録され、クエリは古いバージョンのDictionaryを使用して続行できます。Dictionaryの更新が成功した場合、古いバージョンのDictionaryはアトミックに置き換えられます。

設定の例:

<CloudDetails />

``` xml
<dictionary>
    ...
    <lifetime>300</lifetime>
    ...
</dictionary>

または

CREATE DICTIONARY (...)
...
LIFETIME(300)
...

<lifetime>0</lifetime> (LIFETIME(0)) の設定は、Dictionaryの更新を防ぎます。

更新のための時間間隔を設定することができ、ClickHouseはこの範囲内で均一にランダムな時間を選びます。これは、多数のサーバーで更新する際に、Dictionaryソースへの負荷を分散するために必要です。

設定の例:

<dictionary>
    ...
    <lifetime>
        <min>300</min>
        <max>360</max>
    </lifetime>
    ...
</dictionary>

または

LIFETIME(MIN 300 MAX 360)

もし<min>0</min>および<max>0</max>の場合、ClickHouseはタイムアウトによってDictionaryをリロードしません。 この場合、Dictionaryの構成ファイルが変更された場合やSYSTEM RELOAD DICTIONARYコマンドが実行された場合には、ClickHouseはDictionaryを早くリロードすることがあります。

Dictionaryを更新する際、ClickHouseサーバーはソースのタイプに応じて異なるロジックを適用します。

  • テキストファイルの場合、変更された時間を確認します。以前に記録された時間と異なる場合は、Dictionaryが更新されます。
  • MySQLソースの場合、SHOW TABLE STATUSクエリを使用して変更された時間が確認されますMySQL 8の場合、MySQLでメタ情報キャッシュを無効にする必要があります: set global information_schema_stats_expiry=0)。
  • 他のソースからのDictionaryはデフォルトで毎回更新されます。

他のソースODBC、PostgreSQL、ClickHouseなどの場合、実際に変更された場合にのみDictionaryを更新するクエリを設定できます。これを行うには、次の手順を実行します:

  • Dictionaryテーブルには、ソースデータが更新されると常に変更されるフィールドが必要です。
  • ソースの設定には、変更されるフィールドを取得するクエリを指定する必要があります。ClickHouseサーバーはクエリの結果を行として解釈し、この行が以前の状態と異なる場合にDictionaryを更新します。クエリはソースの設定の<invalidate_query>フィールドに指定します。

設定の例:

<dictionary>
    ...
    <odbc>
      ...
      <invalidate_query>SELECT update_time FROM dictionary_source where id = 1</invalidate_query>
    </odbc>
    ...
</dictionary>

または

...
SOURCE(ODBC(... invalidate_query 'SELECT update_time FROM dictionary_source where id = 1'))
...

CacheComplexKeyCacheSSDCache、および SSDComplexKeyCache Dictionaryは同期および非同期更新の両方をサポートしています。

また、FlatHashedComplexKeyHashed Dictionaryでは、前回の更新後に変更されたデータのみをリクエストすることが可能です。update_fieldがDictionaryソースの設定の一部として指定されている場合、前回の更新時刻秒単位の値がデータリクエストに追加されます。ソースタイプExecutable, HTTP, MySQL, PostgreSQL, ClickHouse, または ODBCに応じて、更新前にupdate_fieldに異なるロジックが適用されます。

  • ソースがHTTPの場合、update_fieldはクエリパラメータとして最後の更新時刻をパラメータ値として追加されます。
  • ソースがExecutableの場合、update_fieldは実行可能スクリプトの引数として最後の更新時刻を引数値として追加されます。
  • ソースがClickHouse、MySQL、PostgreSQL、ODBCの場合、update_fieldはSQLクエリの最高レベルでのWHERE条件として、最後の更新時刻と大なりまたは等しいもので比較されます。デフォルトでは、このWHERE条件はSQLクエリの最上位でチェックされます。代替として、クエリ内の任意の他のWHERE句で{condition}キーワードを使用して条件をチェックできます。例:
    ...
    SOURCE(CLICKHOUSE(...
        update_field 'added_time'
        QUERY '
            SELECT my_arr.1 AS x, my_arr.2 AS y, creation_time
            FROM (
                SELECT arrayZip(x_arr, y_arr) AS my_arr, creation_time
                FROM dictionary_source
                WHERE {condition}
            )'
    ))
    ...
    

update_fieldオプションが設定されている場合、追加のオプションupdate_lagを設定できます。update_lagオプションの値は前回の更新時刻から差し引かれた後に更新されたデータがリクエストされます。

設定の例:

<dictionary>
    ...
        <clickhouse>
            ...
            <update_field>added_time</update_field>
            <update_lag>15</update_lag>
        </clickhouse>
    ...
</dictionary>

または

...
SOURCE(CLICKHOUSE(... update_field 'added_time' update_lag 15))
...

Dictionary Sources

DictionaryはさまざまなソースからClickHouseに接続できます。

Dictionaryがxmlファイルを使用して構成されている場合、構成は次のようになります:

<clickhouse>
  <dictionary>
    ...
    <source>
      <source_type>
        <!-- ソースの設定 -->
      </source_type>
    </source>
    ...
  </dictionary>
  ...
</clickhouse>

DDL-queryの場合、上記の構成は次のようになります:

CREATE DICTIONARY dict_name (...)
...
SOURCE(SOURCE_TYPE(param1 val1 ... paramN valN)) -- ソースの設定
...

ソースはsourceセクションで構成されます。

ソースタイプローカルファイル実行ファイルHTTP(S)ClickHouseのためのオプション設定が利用可能です:

<source>
  <file>
    <path>/opt/dictionaries/os.tsv</path>
    <format>TabSeparated</format>
  </file>
  <settings>
      <format_csv_allow_single_quotes>0</format_csv_allow_single_quotes>
  </settings>
</source>

または

SOURCE(FILE(path './user_files/os.tsv' format 'TabSeparated'))
SETTINGS(format_csv_allow_single_quotes = 0)

ソースの種類 (source_type):

Local File

設定の例:

<source>
  <file>
    <path>/opt/dictionaries/os.tsv</path>
    <format>TabSeparated</format>
  </file>
</source>

または

SOURCE(FILE(path './user_files/os.tsv' format 'TabSeparated'))

設定フィールド:

  • path ファイルへの絶対パス。
  • format ファイルの形式。Formatsで説明されているすべての形式がサポートされています。

DDLコマンドCREATE DICTIONARY ...)でソースFILEを持つDictionaryを作成する際には、ソースファイルをuser_filesディレクトリに配置して、ClickHouseード上の任意のファイルにデータベースユーザーがアクセスするのを防ぐ必要があります。

関連項目

Executable File

実行ファイルの処理は、Dictionaryがメモリ内にどのように保存されているかに依存します。Dictionaryがcachecomplex_key_cacheを使用して保存されている場合、ClickHouseはSTDINにリクエストを送信して必要なキーをリクエストします。それ以外の場合、ClickHouseは実行ファイルを開始し、その出力をDictionaryデータとして扱います。

設定の例:

<source>
    <executable>
        <command>cat /opt/dictionaries/os.tsv</command>
        <format>TabSeparated</format>
        <implicit_key>false</implicit_key>
    </executable>
</source>

設定フィールド:

  • command — 実行可能ファイルへの絶対パス、またはコマンドディレクトリがPATHにある場合はファイル名。
  • format — ファイルの形式。Formatsで説明されているすべての形式がサポートされています。
  • command_termination_timeout — 実行可能スクリプトはメインの読書-書き込みループを含むべきです。Dictionaryが破棄された後、パイプが閉じられ、ClickHouseが子プロセスにSIGTERMシグナルを送信する前に、実行可能ファイルはcommand_termination_timeout秒でシャットダウンさせられます。デフォルト値は10です。オプションのパラメータ。
  • command_read_timeout - コマンドのstdoutからデータを読み取るタイムアウトをミリ秒単位で指定します。デフォルト値は10000です。オプションのパラメータ。
  • command_write_timeout - コマンドのstdinにデータを書き込むタイムアウトをミリ秒単位で指定します。デフォルト値は10000です。オプションのパラメータ。
  • implicit_key — 実行可能ソースファイルは値のみを返すことができ、要求されたキーとの対応は、結果内の行の順序によって暗黙的に決定されます。デフォルト値はfalseです。
  • execute_direct - execute_direct = 1の場合、commanduser_scripts_pathで指定されたuser_scriptsフォルダ内で検索されます。追加のスクリプト引数は空白区切りで指定できます。例: script_name arg1 arg2execute_direct = 0の場合、commandbin/sh -cの引数として渡されます。デフォルト値は0です。オプションのパラメータ。
  • send_chunk_header - 処理するデータチャンクへの行数を送信するかどうかを制御します。オプション。デフォルト値はfalseです。

このDictionaryソースはXML構成でのみ設定できます。DDLを介して実行可能ソースを持つDictionaryを作成することはできません。さもないと、データベースユーザーはClickHouseードで任意のバイナリを実行することができてしまいます。

Executable Pool

実行プールは多くのプロセスプールからデータをロードすることを許可します。このソースは、ソースからすべてのデータをロードする必要があるDictionaryレイアウトでは動作しません。実行プールは、Dictionaryがcachecomplex_key_cachessd_cachecomplex_key_ssd_cachedirect、またはcomplex_key_directレイアウトを使用して保存されている場合に動作します。

実行プールは、指定されたコマンドでプロセスのプールを開始し、それらが終了するまでそれらを保持します。プログラムは、利用可能な限りSTDINからデータを読み取り、結果をSTDOUTに出力する必要があります。次のデータブロックをSTDINで待つことができます。ClickHouseはデータの処理後にSTDINを閉じませんが、必要に応じて新しいデータチャンクをパイプします。実行可能スクリプトは、この方法でのデータ処理に対応できるように準備する必要があります - STDINをポーリングし、データを早期にSTDOUTにフラッシュする必要があります。

設定の例:

<source>
    <executable_pool>
        <command>while read key; do printf "$key\tData for key $key\n"; done</command>
        <format>TabSeparated</format>
        <pool_size>10</pool_size>
        <max_command_execution_time>10</max_command_execution_time>
        <implicit_key>false</implicit_key>
    </executable_pool>
</source>

設定フィールド:

  • command — 実行可能ファイルへの絶対パス、またはプログラムディレクトリがPATHに書かれている場合はファイル名。
  • format — ファイルの形式。Formatsで説明されているすべての形式がサポートされています。
  • pool_size — プールのサイズ。pool_sizeとして0が指定された場合、プールサイズの制限はありません。デフォルト値は16です。
  • command_termination_timeout — 実行可能スクリプトはメイン読取書込ループを含む必要があります。Dictionaryが破棄された後、パイプは閉じられ、実行可能ファイルはClickHouseが子プロセスへSIGTERMシグナルを送信する前にcommand_termination_timeout秒でシャットダウンすることになります。秒単位で指定します。デフォルト値は10です。オプションのパラメータ。
  • max_command_execution_time — データブロックを処理するための最大実行可能スクリプトコマンド実行時間。秒単位で指定します。デフォルト値は10です。オプションのパラメータ。
  • command_read_timeout - コマンドstdoutからデータを読み取るタイムアウトをミリ秒単位で指定します。デフォルト値は10000です。オプションのパラメータ。
  • command_write_timeout - コマンドstdinにデータを書き込むタイムアウトをミリ秒単位で指定します。デフォルト値は10000です。オプションのパラメータ。
  • implicit_key — 実行可能ソースファイルは値のみを返すことができ、要求されたキーとの対応は、結果内の行の順序によって暗黙的に決定されます。デフォルト値はfalseです。オプションのパラメータ。
  • execute_direct - execute_direct = 1の場合、commanduser_scripts_pathで指定されたuser_scriptsフォルダ内で検索されます。追加のスクリプト引数は空白区切りで指定できます。例: script_name arg1 arg2execute_direct = 0の場合、commandbin/sh -cの引数として渡されます。デフォルト値は1です。オプションのパラメータ。
  • send_chunk_header - どのようにして行数を処理するデータチャンクに送信するかを制御します。オプション。デフォルト値はfalseです。

このDictionaryソースはXML構成でのみ設定できます。DDLを介して実行可能ソースを持つDictionaryを作成することはできません。さもないと、データベースユーザーはClickHouseードで任意のバイナリを実行することができてしまいます。

HTTP(S)

HTTP(S)サーバーとの作業は、Dictionaryがメモリーにどのように保存されているかに依存します。Dictionaryがcachecomplex_key_cacheを使用して保存されている場合、ClickHouseはPOSTメソッドを通じて必要なキーを要求します。

設定の例:

<source>
    <http>
        <url>http://[::1]/os.tsv</url>
        <format>TabSeparated</format>
        <credentials>
            <user>user</user>
            <password>password</password>
        </credentials>
        <headers>
            <header>
                <name>API-KEY</name>
                <value>key</value>
            </header>
        </headers>
    </http>
</source>

または

SOURCE(HTTP(
    url 'http://[::1]/os.tsv'
    format 'TabSeparated'
    credentials(user 'user' password 'password')
    headers(header(name 'API-KEY' value 'key'))
))

ClickHouseがHTTPSリソースにアクセスするためには、openSSLをサーバー構成に設定する必要があります。

設定フィールド:

  • url ソースURL。
  • format ファイルの形式。Formatsで説明されているすべての形式がサポートされています。
  • credentials 基本的なHTTP認証。オプションのパラメータ。
  • user 認証に必要なユーザー名。
  • password 認証に必要なパスワード。
  • headers HTTPリクエストに使用するすべてのカスタムHTTPヘッダエントリ。オプションのパラメータ。
  • header 単一のHTTPヘッダエントリ。
  • name リクエスト送信時に使用する識別子名。
  • value 特定の識別子名に設定された値。

DDLコマンドCREATE DICTIONARY ...を使用してDictionaryを作成すると、HTTP Dictionaryのリモートホストは任意のHTTPサーバーにアクセスしないようにremote_url_allow_hostsセクションの内容に対してチェックされます。

DBMS

ODBC

ODBCドライバがある任意のデータベースに接続できます。

設定の例:

<source>
    <odbc>
        <db>DatabaseName</db>
        <table>ShemaName.TableName</table>
        <connection_string>DSN=some_parameters</connection_string>
        <invalidate_query>SQL_QUERY</invalidate_query>
        <query>SELECT id, value_1, value_2 FROM ShemaName.TableName</query>
    </odbc>
</source>

または

SOURCE(ODBC(
    db 'DatabaseName'
    table 'SchemaName.TableName'
    connection_string 'DSN=some_parameters'
    invalidate_query 'SQL_QUERY'
    query 'SELECT id, value_1, value_2 FROM db_name.table_name'
))

設定フィールド:

  • db データベースの名前。接続文字列内にデータベース名が設定されている場合は省略可能。
  • table テーブルの名前とスキーマ。
  • connection_string 接続文字列。
  • invalidate_query Dictionaryの状態を確認するためのクエリ。オプションのパラメータ。Refresh dictionary data using LIFETIMEセクションを参照してください。
  • query カスタムクエリ。オプションのパラメータ。

:::note tablequeryフィールドは一緒に使用できません。そして、tableまたはqueryフィールドのどちらか一方を指定する必要があります。 :::

ClickHouseはODBCドライバからクォート記号を受け取り、ドライバへのクエリ内で設定をすべてクォートしますので、テーブル名はデータベース内のテーブル名のケースに応じて設定する必要があります。

Oracle使用時にはエンコーディングに問題がある場合は、該当するFAQを参照してください。

ODBCDictionary機能の既知の脆弱性

:::note ODBCドライバのServername接続パラメータを通じてデータベースに接続する際に置き換えが生じる可能性があります。この場合、odbc.iniからのUSERNAMEPASSWORDの値がリモートサーバーに送信されるため、これらが漏洩する可能性があります。 :::

安全でない使用の例

PostgreSQL用にunixODBCを設定します。/etc/odbc.iniの内容:

[gregtest]
Driver = /usr/lib/psqlodbca.so
Servername = localhost
PORT = 5432
DATABASE = test_db
#OPTION = 3
USERNAME = test
PASSWORD = test

この後、次のようなクエリを実行すると

SELECT * FROM odbc('DSN=gregtest;Servername=some-server.com', 'test_db');

ODBCドライバはodbc.iniUSERNAMEPASSWORDの値をsome-server.comに送信します。

PostgreSQLへの接続例

Ubuntu OS。

unixODBCとPostgreSQL用ODBCドライバをインストールします:

$ sudo apt-get install -y unixodbc odbcinst odbc-postgresql

/etc/odbc.iniまたはClickHouseを実行しているユーザーでサインインしている場合は~/.odbc.ini)を設定します:

    [DEFAULT]
    Driver = myconnection

    [myconnection]
    Description         = PostgreSQL connection to my_db
    Driver              = PostgreSQL Unicode
    Database            = my_db
    Servername          = 127.0.0.1
    UserName            = username
    Password            = password
    Port                = 5432
    Protocol            = 9.3
    ReadOnly            = No
    RowVersioning       = No
    ShowSystemTables    = No
    ConnSettings        =

ClickHouseのDictionary設定:

<clickhouse>
    <dictionary>
        <name>table_name</name>
        <source>
            <odbc>
                <!-- 以下のパラメータをconnection_stringで指定できます: -->
                <!-- DSN=myconnection;UID=username;PWD=password;HOST=127.0.0.1;PORT=5432;DATABASE=my_db -->
                <connection_string>DSN=myconnection</connection_string>
                <table>postgresql_table</table>
            </odbc>
        </source>
        <lifetime>
            <min>300</min>
            <max>360</max>
        </lifetime>
        <layout>
            <hashed/>
        </layout>
        <structure>
            <id>
                <name>id</name>
            </id>
            <attribute>
                <name>some_column</name>
                <type>UInt64</type>
                <null_value>0</null_value>
            </attribute>
        </structure>
    </dictionary>
</clickhouse>

または

CREATE DICTIONARY table_name (
    id UInt64,
    some_column UInt64 DEFAULT 0
)
PRIMARY KEY id
SOURCE(ODBC(connection_string 'DSN=myconnection' table 'postgresql_table'))
LAYOUT(HASHED())
LIFETIME(MIN 300 MAX 360)

ドライバがあるライブラリへのフルパスを指定するためにodbc.iniを編集する必要があるかもしれません:DRIVER=/usr/local/lib/psqlodbcw.so

MS SQL Serverへの接続例

Ubuntu OS。

MS SQLに接続するためのODBCドライバのインストール:

$ sudo apt-get install tdsodbc freetds-bin sqsh

ドライバの設定:

    $ cat /etc/freetds/freetds.conf
    ...

    [MSSQL]
    host = 192.168.56.101
    port = 1433
    tds version = 7.0
    client charset = UTF-8

    # TDS接続のテスト
    $ sqsh -S MSSQL -D database -U user -P password


    $ cat /etc/odbcinst.ini

    [FreeTDS]
    Description     = FreeTDS
    Driver          = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
    Setup           = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
    FileUsage       = 1
    UsageCount      = 5

    $ cat /etc/odbc.ini
    # $ cat ~/.odbc.ini # ClickHouseを実行しているユーザーでサインインしている場合

    [MSSQL]
    Description     = FreeTDS
    Driver          = FreeTDS
    Servername      = MSSQL
    Database        = test
    UID             = test
    PWD             = test
    Port            = 1433


    # オプションODBC接続のテストisqlツールを使用するには[unixodbc](https://packages.debian.org/sid/unixodbc)パッケージをインストールします)
    $ isql -v MSSQL "user" "password"

備考:

  • 特定のSQL Serverバージョンでサポートされる最も早いTDSバージョンを確認するには、製品のドキュメントを参照するか、MS-TDS製品動作を見てください。

ClickHouseでのDictionaryの設定:

<clickhouse>
    <dictionary>
        <name>test</name>
        <source>
            <odbc>
                <table>dict</table>
                <connection_string>DSN=MSSQL;UID=test;PWD=test</connection_string>
            </odbc>
        </source>

        <lifetime>
            <min>300</min>
            <max>360</max>
        </lifetime>

        <layout>
            <flat />
        </layout>

        <structure>
            <id>
                <name>k</name>
            </id>
            <attribute>
                <name>s</name>
                <type>String</type>
                <null_value></null_value>
            </attribute>
        </structure>
    </dictionary>
</clickhouse>

または

CREATE DICTIONARY test (
    k UInt64,
    s String DEFAULT ''
)
PRIMARY KEY k
SOURCE(ODBC(table 'dict' connection_string 'DSN=MSSQL;UID=test;PWD=test'))
LAYOUT(FLAT())
LIFETIME(MIN 300 MAX 360)

MySQL

設定の例:

<source>
  <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>db_name</db>
      <table>table_name</table>
      <where>id=10</where>
      <invalidate_query>SQL_QUERY</invalidate_query>
      <fail_on_connection_loss>true</fail_on_connection_loss>
      <query>SELECT id, value_1, value_2 FROM db_name.table_name</query>
  </mysql>
</source>

または

SOURCE(MYSQL(
    port 3306
    user 'clickhouse'
    password 'qwerty'
    replica(host 'example01-1' priority 1)
    replica(host 'example01-2' priority 1)
    db 'db_name'
    table 'table_name'
    where 'id=10'
    invalidate_query 'SQL_QUERY'
    fail_on_connection_loss 'true'
    query 'SELECT id, value_1, value_2 FROM db_name.table_name'
))

設定フィールド:

  • port MySQLサーバーのポート。すべてのレプリカに対して、または個別に<replica>内)指定できます。

  • user MySQLユーザー名。すべてのレプリカに対して、または個別に<replica>内)指定できます。

  • password MySQLユーザーのパスワード。すべてのレプリカに対して、または個別に<replica>内)指定できます。

  • replica レプリカ構成のセクション。複数のセクションを作成できます。

      - `replica/host`  MySQLホスト。
      - `replica/priority`  レプリカの優先度。ClickHouseが接続を試みる際は、優先度に基づいてレプリカを巡回します。数が少ないほど優先度が高くなります。
    
  • db データベースの名前。

  • table テーブルの名前。

  • where 選択条件。条件の構文はMySQLのWHERE句と同じで、たとえばid > 10 AND id < 20のようになります。オプションのパラメータ。

  • invalidate_query Dictionaryの状態を確認するためのクエリ。オプションのパラメータ。Refresh dictionary data using LIFETIMEセクションを参照してください。

  • fail_on_connection_loss 接続喪失時のサーバーの動作を制御する構成パラメータ。trueの場合、クライアントとサーバー間の接続が失われた場合に即座に例外がスローされます。falseの場合、例外がスローされる前にClickHouseサーバーはクエリを3回再試行します。再試行により応答時間が増加することに注意してください。デフォルト値: false

  • query カスタムクエリ。オプションのパラメータ。

:::note tableまたはwhereフィールドは、queryフィールドと一緒に使用することはできません。そして、tableまたはqueryフィールドのどちらか一方を指定する必要があります。 :::

:::note 明示的なパラメータsecureはありません。SSL接続を確立する際は、セキュリティが必須です。 :::

MySQLはローカルホスト上でソケットを介して接続できます。この場合、hostsocketを設定します。

設定の例:

<source>
  <mysql>
      <host>localhost</host>
      <socket>/path/to/socket/file.sock</socket>
      <user>clickhouse</user>
      <password>qwerty</password>
      <db>db_name</db>
      <table>table_name</table>
      <where>id=10</where>
      <invalidate_query>SQL_QUERY</invalidate_query>
      <fail_on_connection_loss>true</fail_on_connection_loss>
	  <query>SELECT id, value_1, value_2 FROM db_name.table_name</query>
  </mysql>
</source>

または

SOURCE(MYSQL(
    host 'localhost'
    socket '/path/to/socket/file.sock'
    user 'clickhouse'
    password 'qwerty'
    db 'db_name'
    table 'table_name'
    where 'id=10'
    invalidate_query 'SQL_QUERY'
    fail_on_connection_loss 'true'
    query 'SELECT id, value_1, value_2 FROM db_name.table_name'
))

ClickHouse

設定の例:

<source>
    <clickhouse>
        <host>example01-01-1</host>
        <port>9000</port>
        <user>default</user>
        <password></password>
        <db>default</db>
        <table>ids</table>
        <where>id=10</where>
        <secure>1</secure>
		<query>SELECT id, value_1, value_2 FROM default.ids</query>
    </clickhouse>
</source>

または

SOURCE(CLICKHOUSE(
    host 'example01-01-1'
    port 9000
    user 'default'
    password ''
    db 'default'
    table 'ids'
    where 'id=10'
    secure 1
	query 'SELECT id, value_1, value_2 FROM default.ids'
));

設定フィールド:

  • host ClickHouseホスト。ローカルホストの場合、クエリはネットワークを使用せずに処理されます。フォールトトレランスを向上させるために、分散テーブルを作成し、それを後続の設定に入力することができます。
  • port ClickHouseサーバーのポート。
  • user ClickHouseユーザーの名前。
  • password ClickHouseユーザーのパスワード。
  • db データベースの名前。
  • table テーブルの名前。
  • where 選択条件。省略可能です。
  • invalidate_query Dictionaryの状態を確認するためのクエリ。オプションのパラメータ。Refresh dictionary data using LIFETIMEセクションを参照してください。
  • secure - 接続にSSLを使用。
  • query カスタムクエリ。オプションのパラメータ。

:::note tableまたはwhereフィールドは、queryフィールドと一緒に使用することはできません。そして、tableまたはqueryフィールドのどちらか一方を指定する必要があります。 :::

MongoDB

設定の例:

<source>
    <mongodb>
        <host>localhost</host>
        <port>27017</port>
        <user></user>
        <password></password>
        <db>test</db>
        <collection>dictionary_source</collection>
        <options>ssl=true</options>
    </mongodb>
</source>

または

<source>
    <mongodb>
        <uri>mongodb://localhost:27017/test?ssl=true</uri>
        <collection>dictionary_source</collection>
    </mongodb>
</source>

または

SOURCE(MONGODB(
    host 'localhost'
    port 27017
    user ''
    password ''
    db 'test'
    collection 'dictionary_source'
    options 'ssl=true'
))

設定フィールド:

  • host MongoDBホスト。
  • port MongoDBサーバーのポート。
  • user MongoDBユーザー名。
  • password MongoDBユーザーのパスワード。
  • db データベースの名前。
  • collection コレクションの名前。
  • options - MongoDB接続文字列オプションオプションのパラメータ

または

SOURCE(MONGODB(
    uri 'mongodb://localhost:27017/clickhouse'
    collection 'dictionary_source'
))

設定フィールド:

  • uri - 接続を確立するためのURI。
  • collection コレクションの名前。

エンジンの詳細情報

Redis

設定の例:

<source>
    <redis>
        <host>localhost</host>
        <port>6379</port>
        <storage_type>simple</storage_type>
        <db_index>0</db_index>
    </redis>
</source>

または

SOURCE(REDIS(
    host 'localhost'
    port 6379
    storage_type 'simple'
    db_index 0
))

設定フィールド:

  • host Redisホスト。
  • port Redisサーバーのポート。
  • storage_type キーを操作するために使用される内部のRedisストレージ構造。simpleは単純なソースおよび単一のキーソースにハッシュされたものに、hash_mapは2つのキーを持つハッシュされたソースに使用されます。範囲指定されたソースや、複雑なキーを持つキャッシュソースはサポートされていません。省略可能、デフォルト値はsimpleです。
  • db_index 特定の数値インデックスのRedis論理データベース。省略可能、デフォルト値は0です。

Cassandra

設定の例:

<source>
    <cassandra>
        <host>localhost</host>
        <port>9042</port>
        <user>username</user>
        <password>qwerty123</password>
        <keyspase>database_name</keyspase>
        <column_family>table_name</column_family>
        <allow_filtering>1</allow_filtering>
<partition_key_prefix>1</partition_key_prefix>
<consistency>One</consistency>
<where>"SomeColumn" = 42</where>
<max_threads>8</max_threads>
<query>SELECT id, value_1, value_2 FROM database_name.table_name</query>
</cassandra>
</source>

設定項目:

  • host Cassandraホスト、またはカンマ区切りのホストリスト。
  • port Cassandraサーバーのポート。指定されていない場合はデフォルトポート9042が使用されます。
  • user Cassandraユーザーの名前。
  • password Cassandraユーザーのパスワード。
  • keyspace キースペース(データベース)の名前。
  • column_family カラムファミリー(テーブル)の名前。
  • allow_filtering クラスタリングキーカラムに対する潜在的に高コストな条件を許可するかどうかを示すフラグ。デフォルト値は1です。
  • partition_key_prefix Cassandraテーブルの主キー内のパーティションキーカラムの数。複合キーDictionaryに必要です。Dictionary定義のキーの順序はCassandraと同じでなければなりません。デフォルト値は1最初のキーはパーティションキーで、他のキーはクラスタリングキーです。
  • consistency 一貫性レベル。可能な値は:One, Two, Three, All, EachQuorum, Quorum, LocalQuorum, LocalOne, Serial, LocalSerialです。デフォルト値はOneです。
  • where オプションの選択条件。
  • max_threads 複合キーDictionaryで複数のパーティションからデータをロードするために使用する最大スレッド数。
  • query カスタムクエリ。オプションのパラメータ。

:::note column_familyまたはwhereフィールドはqueryフィールドと一緒に使用できません。また、column_familyまたはqueryのいずれか一方のフィールドを必ず宣言する必要があります。 :::

PostgreSQL

設定の例:

<source>
  <postgresql>
      <host>postgresql-hostname</host>
      <port>5432</port>
      <user>clickhouse</user>
      <password>qwerty</password>
      <db>db_name</db>
      <table>table_name</table>
      <where>id=10</where>
      <invalidate_query>SQL_QUERY</invalidate_query>
      <query>SELECT id, value_1, value_2 FROM db_name.table_name</query>
  </postgresql>
</source>

または

SOURCE(POSTGRESQL(
    port 5432
    host 'postgresql-hostname'
    user 'postgres_user'
    password 'postgres_password'
    db 'db_name'
    table 'table_name'
    replica(host 'example01-1' port 5432 priority 1)
    replica(host 'example01-2' port 5432 priority 2)
    where 'id=10'
    invalidate_query 'SQL_QUERY'
    query 'SELECT id, value_1, value_2 FROM db_name.table_name'
))

設定項目:

  • host PostgreSQLサーバーのホスト。すべてのレプリカに対して、またはそれぞれ個別に指定できます<replica>内)。
  • port PostgreSQLサーバーのポート。すべてのレプリカに対して、またはそれぞれ個別に指定できます<replica>内)。
  • user PostgreSQLユーザーの名前。すべてのレプリカに対して、またはそれぞれ個別に指定できます<replica>内)。
  • password PostgreSQLユーザーのパスワード。すべてのレプリカに対して、またはそれぞれ個別に指定できます<replica>内)。
  • replica レプリカ設定セクションが複数あります:
    • replica/host PostgreSQLホスト。
    • replica/port PostgreSQLポート。
    • replica/priority レプリカの優先順位。接続を試みる際、ClickHouseは優先順位の順にレプリカを試みます。数字が小さいほど、優先順位が高くなります。
  • db データベースの名前。
  • table テーブルの名前。
  • where 選択基準。条件の構文はPostgreSQLのWHERE句と同じです。例えばid > 10 AND id < 20。オプションのパラメータ。
  • invalidate_query Dictionaryの状態を確認するためのクエリ。オプションのパラメータ。詳細はRefreshing dictionary data using LIFETIMEセクションを参照してください。
  • query カスタムクエリ。オプションのパラメータ。

:::note tableまたはwhereフィールドはqueryフィールドと一緒に使用できません。また、tableまたはqueryのいずれか一方のフィールドを必ず宣言する必要があります。 :::

Null

ダミーのDictionaryを作成するために使用できる特別なソースです。このようなDictionaryは、テストや分散テーブルのデータとクエリードが分離されたセットアップで役立ちます。

CREATE DICTIONARY null_dict (
    id              UInt64,
    val             UInt8,
    default_val     UInt8 DEFAULT 123,
    nullable_val    Nullable(UInt8)
)
PRIMARY KEY id
SOURCE(NULL())
LAYOUT(FLAT())
LIFETIME(0);

Dictionary Key and Fields

structure句はクエリ用のDictionaryキーとフィールドを記述します。

XMLの記述:

<dictionary>
    <structure>
        <id>
            <name>Id</name>
        </id>

        <attribute>
            <!-- 属性のパラメータ -->
        </attribute>

        ...

    </structure>
</dictionary>

属性は以下の要素で記述されます:

  • <id> — キーカラム
  • <attribute> — データカラム: 複数の属性を持つことができます。

DDLクエリ:

CREATE DICTIONARY dict_name (
    Id UInt64,
    -- attributes
)
PRIMARY KEY Id
...

属性はクエリ本文で記述されます:

  • PRIMARY KEY — キーカラム
  • AttrName AttrType — データカラム。複数の属性を持つことができます。

Key

ClickHouseは以下のタイプのキーをサポートします:

  • 数値キー。UInt64<id>タグまたはPRIMARY KEYキーワードで定義されます。
  • 複合キー。異なるタイプの値の集合。<key>タグまたはPRIMARY KEYキーワードで定義されます。

XML構造には<id>または<key>のいずれかを含めることができます。DDLクエリにはPRIMARY KEYが1つ含まれている必要があります。

:::note キーを属性として記述してはいけません。 :::

Numeric Key

タイプ: UInt64

設定例:

<id>
    <name>Id</name>
</id>

設定項目:

  • name キーを持つカラムの名前。

DDLクエリの例:

CREATE DICTIONARY (
    Id UInt64,
    ...
)
PRIMARY KEY Id
...
  • PRIMARY KEY キーを持つカラムの名前。

Composite Key

キーはあらゆるタイプのフィールドのtupleにすることができます。この場合のレイアウトcomplex_key_hashedまたはcomplex_key_cacheである必要があります。

:::tip 複合キーは単一要素で構成することもできます。これにより、例えば文字列をキーとして使用することが可能になります。 :::

キー構造は<key>要素で設定されます。キー・フィールドはDictionaryのattributesと同じ形式で指定されます。例:

<structure>
    <key>
        <attribute>
            <name>field1</name>
            <type>String</type>
        </attribute>
        <attribute>
            <name>field2</name>
            <type>UInt32</type>
        </attribute>
        ...
    </key>
...

または

CREATE DICTIONARY (
    field1 String,
    field2 String
    ...
)
PRIMARY KEY field1, field2
...

dictGet*関数に対するクエリでは、キーとしてタプルが渡されます。例: dictGetString('dict_name', 'attr_name', tuple('string for field1', num_for_field2))

Attributes

設定例:

<structure>
    ...
    <attribute>
        <name>Name</name>
        <type>ClickHouseDataType</type>
        <null_value></null_value>
        <expression>rand64()</expression>
        <hierarchical>true</hierarchical>
        <injective>true</injective>
        <is_object_id>true</is_object_id>
    </attribute>
</structure>

または

CREATE DICTIONARY somename (
    Name ClickHouseDataType DEFAULT '' EXPRESSION rand64() HIERARCHICAL INJECTIVE IS_OBJECT_ID
)

設定項目:

タグ 説明 必須項目
name カラム名。 はい
type ClickHouseデータタイプ: UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64, UUID, Decimal32, Decimal64, Decimal128, Decimal256,Date, Date32, DateTime, DateTime64, String, Array
ClickHouseはDictionaryから指定されたデータタイプに値をキャストしようとします。例えば、MySQLではフィールドはTEXTVARCHAR、またはBLOBであるかもしれませんが、ClickHouseではStringとしてアップロードできます。
Nullableは現在、Flat, Hashed, ComplexKeyHashed, Direct, ComplexKeyDirect, RangeHashed, Polygon, Cache, ComplexKeyCache, SSDCache, SSDComplexKeyCache Dictionaryでサポートされています。IPTrie Dictionaryでは Nullable タイプはサポートされていません。
はい
null_value 存在しない要素のデフォルト値。
例では空の文字列です。NULL 値はNullableタイプのみで使用可能です(上記のタイプの説明を参照)。
はい
expression ClickHouseが値に対して実行する
式はリモートSQLデータベースのカラム名であることができます。したがって、リモートカラムのエイリアスを作成するために使用できます。

デフォルト値: 式なし。
いいえ
hierarchical trueの場合、属性は現在のキーの親キーの値を含みます。階層Dictionaryを参照してください。

デフォルト値: false
いいえ
injective id -> attributeの写像が単射であるかどうかを示すフラグ。
trueの場合、ClickHouseはGROUP BY句の後に単射のDictionaryへの要求を自動的に配置できます。通常、これによりこのような要求の量が大幅に削減されます。

デフォルト値: false
いいえ
is_object_id クエリがObjectIDによるMongoDBドキュメントの実行であるかどうかを示すフラグ。

デフォルト値: false

階層Dictionary

ClickHouseは数値キーを持つ階層Dictionaryをサポートします。

以下のような階層構造を考えてみましょう:

0 (共通の親)
│
├── 1 (ロシア)
│   │
│   └── 2 (モスクワ)
│       │
│       └── 3 (中心)
│
└── 4 (イギリス)
    │
    └── 5 (ロンドン)

この階層は次のDictionaryテーブルで表現できます。

region_id parent_region region_name
1 0 ロシア
2 1 モスクワ
3 2 中心
4 0 イギリス
5 4 ロンドン

このテーブルには、parent_regionカラムがあり、それは要素の最も近い親のキーを含んでいます。

ClickHouseは外部Dictionary属性に対して階層プロパティをサポートしています。このプロパティを使用すると、上記のように階層Dictionaryを設定できます。

dictGetHierarchy関数を使用すると、要素の親チェーンを取得できます。

我々の例では、Dictionaryの構造は次のようになります:

<dictionary>
    <structure>
        <id>
            <name>region_id</name>
        </id>

        <attribute>
            <name>parent_region</name>
            <type>UInt64</type>
            <null_value>0</null_value>
            <hierarchical>true</hierarchical>
        </attribute>

        <attribute>
            <name>region_name</name>
            <type>String</type>
            <null_value></null_value>
        </attribute>

    </structure>
</dictionary>

ポリゴンDictionary

ポリゴンDictionaryは、指定された点を含むポリゴンの検索を効率的に行うことができます。 例えば、地理座標で都市地域を定義することができます。

ポリゴンDictionary設定の例:

<dictionary>
    <structure>
        <key>
            <attribute>
                <name>key</name>
                <type>Array(Array(Array(Array(Float64))))</type>
            </attribute>
        </key>

        <attribute>
            <name>name</name>
            <type>String</type>
            <null_value></null_value>
        </attribute>

        <attribute>
            <name>value</name>
            <type>UInt64</type>
            <null_value>0</null_value>
        </attribute>
    </structure>

    <layout>
        <polygon>
            <store_polygon_key_column>1</store_polygon_key_column>
        </polygon>
    </layout>

    ...
</dictionary>

対応するDDLクエリ:

CREATE DICTIONARY polygon_dict_name (
    key Array(Array(Array(Array(Float64)))),
    name String,
    value UInt64
)
PRIMARY KEY key
LAYOUT(POLYGON(STORE_POLYGON_KEY_COLUMN 1))
...

ポリゴンDictionaryを設定する際、キーは以下のタイプのいずれかでなければなりません:

  • 単純なポリゴン: 点の配列。
  • マルチポリゴン: ポリゴンの配列。各ポリゴンは点の2次元配列です。この配列の最初の要素がポリゴンの外部境界であり、後続の要素はそれから除外される領域を指定します。

点は、それぞれの座標を表す配列またはタプルとして指定できます。現在の実装では、2次元点のみがサポートされています。

ユーザーは、ClickHouseがサポートするすべてのフォーマットで独自のデータをアップロードできます。

次の3種類のインメモリストレージが利用可能です:

  • POLYGON_SIMPLE: クエリごとにすべてのポリゴンを線形に通過し、追加のインデックスを使用せずにそれぞれの所属を確認するナイーブな実装です。

  • POLYGON_INDEX_EACH: 地理的地域向けに最適化されていて、ほとんどの場合すばやく所属が確認できるように、各ポリゴンに個別のインデックスが構築されます。 また、エリアにグリッドが重ねられ、候補となるポリゴンの数を大幅に絞り込みます。 グリッドは、セルを16等分に再帰的に分割することで作成され、2つのパラメータで設定されます。 分割は、再帰の深さがMAX_DEPTHに達するか、セルが横断するポリゴンの数がMIN_INTERSECTIONSに達すると終了します。 クエリに応答するには、対応するセルの中に保存されているポリゴンのインデックスにアクセスされます。

  • POLYGON_INDEX_CELL: 上記のグリッドも作成されます。同じオプションが利用可能です。各シートセルに対して、その中に入るポリゴン片すべてに対してインデックスが構築されており、クエリに迅速に応答できます。

  • POLYGON: POLYGON_INDEX_CELLの別名です。

Dictionaryクエリは、Dictionaryを操作するための標準的な関数を通じて行われます。 重要な違いは、ここではキーポイントが指定され、指定された点を含む最小のポリゴンが見つかる点です。

上で定義されたDictionaryと連動する例:

CREATE TABLE points (
    x Float64,
    y Float64
)
...
SELECT tuple(x, y) AS key, dictGet(dict_name, 'name', key), dictGet(dict_name, 'value', key) FROM points ORDER BY x, y;

最後のコマンドを実行した結果、pointsテーブル内の各ポイントに対して、それを含む最小領域ポリゴンが見つかり、要求された属性が出力されます。

ポリゴンDictionaryからSELECTクエリを介してカラムを読み取ることができます。Dictionary設定または対応するDDLクエリでstore_polygon_key_column = 1をオンにしてください。

クエリ:

CREATE TABLE polygons_test_table
(
    key Array(Array(Array(Tuple(Float64, Float64)))),
    name String
) ENGINE = TinyLog;

INSERT INTO polygons_test_table VALUES ([[[(3, 1), (0, 1), (0, -1), (3, -1)]]], 'Value');

CREATE DICTIONARY polygons_test_dictionary
(
    key Array(Array(Array(Tuple(Float64, Float64)))),
    name String
)
PRIMARY KEY key
SOURCE(CLICKHOUSE(TABLE 'polygons_test_table'))
LAYOUT(POLYGON(STORE_POLYGON_KEY_COLUMN 1))
LIFETIME(0);

SELECT * FROM polygons_test_dictionary;

結果:

┌─key─────────────────────────────┬─name──┐
│ [[[(3,1),(0,1),(0,-1),(3,-1)]]] │ Value │
└─────────────────────────────────┴───────┘

正規表現ツリーDictionary

正規表現ツリーDictionaryは、正規表現のツリーを使ってキーから属性へのマッピングを表現する特別なタイプのDictionaryです。例えば、ユーザーエージェント文字列の解析のようなユースケースは、正規表現ツリーDictionaryで優雅に表現できます。

ClickHouse Open-Sourceでの正規表現ツリーDictionaryの使用

正規表現ツリーディクショナリは、ClickHouseオープンソースで、正規表現ツリーを含むYAMLファイルのパスを提供するYAMLRegExpTreeソースを使用して定義されます。

CREATE DICTIONARY regexp_dict
(
    regexp String,
    name String,
    version String
)
PRIMARY KEY(regexp)
SOURCE(YAMLRegExpTree(PATH '/var/lib/clickhouse/user_files/regexp_tree.yaml'))
LAYOUT(regexp_tree)
...

ディクショナリソースYAMLRegExpTreeは正規表現ツリーの構造を表します。例えば:

- regexp: 'Linux/(\d+[\.\d]*).+tlinux'
  name: 'TencentOS'
  version: '\1'

- regexp: '\d+/tclwebkit(?:\d+[\.\d]*)'
  name: 'Android'
  versions:
    - regexp: '33/tclwebkit'
      version: '13'
    - regexp: '3[12]/tclwebkit'
      version: '12'
    - regexp: '30/tclwebkit'
      version: '11'
    - regexp: '29/tclwebkit'
      version: '10'

この設定は正規表現ツリーノードのリストで構成されています。各ノードは以下の構造を持ちます:

  • regexp: ノードの正規表現。
  • attributes: ユーザー定義ディクショナリアトリビュートのリスト。この例では、2つのアトリビュートnameversionがあります。最初のードは両方のアトリビュートを定義しています。2番目のードはアトリビュートnameのみを定義しています。アトリビュートversionは2番目のードの子ードから提供されます。
    • アトリビュートの値は、バックリファレンスを含むことがあり、正規表現のキャプチャグループを参照します。この例では、最初のノードのアトリビュートversionの値は、正規表現のキャプチャグループ(\d+[\.\d]*)へのバックリファレンス\1を含んでいます。バックリファレンス番号は1から9の範囲で、$1または\1番号1の場合として書かれます。バックリファレンスはクエリ実行中のマッチしたキャプチャグループで置き換えられます。
  • child nodes: 正規表現ツリーードの子のリストで、それぞれが独自のアトリビュートと可能な限りードを持ちます。文字列のマッチングは深さ優先で進行します。もし文字列が正規表現ードと一致すると、ディクショナリはードの子ードとも一致するか確認します。その場合、最も深くマッチしたードのアトリビュートが割り当てられます。子ードのアトリビュートは親ードの同名アトリビュートを上書きします。YAMLファイル内の子ード名は任意で、上記の例ではversionsとされています。

正規表現ツリーディクショナリはdictGetdictGetOrDefault、およびdictGetAll関数を使用してアクセスのみ許可されます。

例:

SELECT dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024');

結果:

┌─dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024')─┐
│ ('Android','12')                                                │
└─────────────────────────────────────────────────────────────────┘

この場合、最初にトップレイヤーの2番目のードの正規表現\d+/tclwebkit(?:\d+[\.\d]*)と一致します。ディクショナリはさらに子ノードを調べ、文字列が3[12]/tclwebkitと一致することも確認します。その結果、アトリビュートnameの値はAndroid(最初のレイヤーで定義)で、アトリビュートversionの値は12(子ノードで定義)となります。

強力なYAML設定ファイルを使用することで、正規表現ツリーディクショナリをユーザーエージェント文字列パーサーとして使用できます。私たちはuap-coreをサポートしており、機能テスト02504_regexp_dictionary_ua_parserでその使用方法を示しています。

アトリビュート値の収集

時には、リーフノードの値だけでなく、マッチした複数の正規表現から値を返すことが有益です。このような場合には、特殊化されたdictGetAll関数を使用できます。ノードに型Tのアトリビュート値がある場合、dictGetAllは0またはそれ以上の値を含むArray(T)を返します。

デフォルトでは、キーごとに返されるマッチ数は制限されません。制限はオプションの第4引数としてdictGetAllに渡すことができます。配列は_トポロジカル順_で埋められ、子ードが親ードの前に来て、兄弟ードはソース内の順序に従います。

例:

CREATE DICTIONARY regexp_dict
(
    regexp String,
    tag String,
    topological_index Int64,
    captured Nullable(String),
    parent String
)
PRIMARY KEY(regexp)
SOURCE(YAMLRegExpTree(PATH '/var/lib/clickhouse/user_files/regexp_tree.yaml'))
LAYOUT(regexp_tree)
LIFETIME(0)
# /var/lib/clickhouse/user_files/regexp_tree.yaml
- regexp: 'clickhouse\.com'
  tag: 'ClickHouse'
  topological_index: 1
  paths:
    - regexp: 'clickhouse\.com/docs(.*)'
      tag: 'ClickHouse Documentation'
      topological_index: 0
      captured: '\1'
      parent: 'ClickHouse'

- regexp: '/docs(/|$)'
  tag: 'Documentation'
  topological_index: 2

- regexp: 'github.com'
  tag: 'GitHub'
  topological_index: 3
  captured: 'NULL'
CREATE TABLE urls (url String) ENGINE=MergeTree ORDER BY url;
INSERT INTO urls VALUES ('clickhouse.com'), ('clickhouse.com/docs/ja'), ('github.com/clickhouse/tree/master/docs');
SELECT url, dictGetAll('regexp_dict', ('tag', 'topological_index', 'captured', 'parent'), url, 2) FROM urls;

結果:

┌─url────────────────────────────────────┬─dictGetAll('regexp_dict', ('tag', 'topological_index', 'captured', 'parent'), url, 2)─┐
│ clickhouse.com                         │ (['ClickHouse'],[1],[],[])                                                            │
│ clickhouse.com/docs/ja                 │ (['ClickHouse Documentation','ClickHouse'],[0,1],['/en'],['ClickHouse'])              │
│ github.com/clickhouse/tree/master/docs │ (['Documentation','GitHub'],[2,3],[NULL],[])                                          │
└────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘

マッチングモード

パターンマッチングの動作は特定のディクショナリ設定で変更できます:

  • regexp_dict_flag_case_insensitive: 大文字小文字を区別しないマッチングを使用(デフォルトはfalse)。個々の表現で(?i)(?-i)で上書き可能。
  • regexp_dict_flag_dotall: .を改行文字とマッチさせる(デフォルトはfalse)。

ClickHouse Cloudでの正規表現ツリーディクショナリの使用

上記のYAMLRegExpTreeソースはClickHouseオープンソースでは動作しますが、ClickHouse Cloudでは動作しません。ClickHouse Cloudで正規表現ツリーディクショナリを使用するには、まずローカルのClickHouseオープンソースでYAMLファイルから正規表現ツリーディクショナリを作成し、dictionariesテーブル関数とINTO OUTFILE句を使用してこのディクショナリをCSVファイルにダンプします。

SELECT * FROM dictionary(regexp_dict) INTO OUTFILE('regexp_dict.csv')

csvファイルの内容は以下の通りです

1,0,"Linux/(\d+[\.\d]*).+tlinux","['version','name']","['\\1','TencentOS']"
2,0,"(\d+)/tclwebkit(\d+[\.\d]*)","['comment','version','name']","['test $1 and $2','$1','Android']"
3,2,"33/tclwebkit","['version']","['13']"
4,2,"3[12]/tclwebkit","['version']","['12']"
5,2,"30/tclwebkit","['version']","['11']"
6,2,"29/tclwebkit","['version']","['10']"

ダンプされたファイルのスキーマは次の通りです:

  • id UInt64: RegexpTreeードのID。
  • parent_id UInt64: ードの親のID。
  • regexp String: 正規表現文字列。
  • keys Array(String): ユーザー定義属性の名前。
  • values Array(String): ユーザー定義属性の値。

ClickHouse Cloudでディクショナリを作成するには、まず以下のテーブル構造でregexp_dictionary_source_tableというテーブルを作成します:

CREATE TABLE regexp_dictionary_source_table
(
    id UInt64,
    parent_id UInt64,
    regexp String,
    keys   Array(String),
    values Array(String)
) ENGINE=Memory;

その後、ローカルCSVを以下のコマンドで更新します

clickhouse client \
    --host MY_HOST \
    --secure \
    --password MY_PASSWORD \
    --query "
    INSERT INTO regexp_dictionary_source_table
    SELECT * FROM input ('id UInt64, parent_id UInt64, regexp String, keys Array(String), values Array(String)')
    FORMAT CSV" < regexp_dict.csv

さらに詳しくはローカルファイルの挿入方法をご覧ください。ソーステーブルを初期化後、テーブルソースで正規表現ツリーを作成できます:

CREATE DICTIONARY regexp_dict
(
    regexp String,
    name String,
    version String
PRIMARY KEY(regexp)
SOURCE(CLICKHOUSE(TABLE 'regexp_dictionary_source_table'))
LIFETIME(0)
LAYOUT(regexp_tree);

埋め込みディクショナリ

ClickHouseはジオベースを操作するための組み込み機能を持っています。

これにより、以下が可能です:

  • 地域のIDを使用して、希望する言語でその名前を取得。
  • 地域のIDを使用して、市、地域、連邦地区、国、大陸のIDを取得。
  • ある地域が別の地域の一部であるかどうかを確認。
  • 親地域のチェーンを取得。

すべての関数は「トランスロカリティ」をサポートしており、地域の所有に関する異なる視点を同時に使用する能力があります。詳細は、「web analyticsディクショナリを操作するための関数」セクションを参照してください。

内部ディクショナリはデフォルトパッケージでは無効です。 それらを有効にするには、サーバー構成ファイルでpath_to_regions_hierarchy_fileおよびpath_to_regions_names_filesのパラメータをコメントアウトします。

ジオベースはテキストファイルから読み込まれます。

regions_hierarchy*.txtファイルをpath_to_regions_hierarchy_fileディレクトリに配置します。この構成パラメータにはデフォルトの地域階層であるregions_hierarchy.txtファイルへのパスを含める必要があり、他のファイル(regions_hierarchy_ua.txt)は同じディレクトリに配置する必要があります。

regions_names_*.txtファイルをpath_to_regions_names_filesディレクトリに配置します。

これらのファイルを自分で作成することもできます。ファイル形式は以下の通りです:

regions_hierarchy*.txt: TabSeparatedヘッダーなし、カラム

  • 地域IDUInt32
  • 親地域IDUInt32
  • 地域タイプ(UInt81 - 大陸、3 - 国、4 - 連邦地区、5 - 地域、6 - 市; 他のタイプには値がない
  • 人口(UInt32)— オプションカラム

regions_names_*.txt: TabSeparatedヘッダーなし、カラム

  • 地域IDUInt32
  • 地域名(String)— タブや改行、エスケープされたものも含めることができません。

RAMに格納するためにフラットな配列が使用されます。このため、IDは100万を超えないようにすべきです。

ディクショナリはサーバーを再起動せずに更新できます。ただし、利用可能なディクショナリのセットは更新されません。 更新のためにファイルの変更時間がチェックされます。ファイルが変更された場合、ディクショナリが更新されます。 変更チェックの間隔はbuiltin_dictionaries_reload_intervalパラメータで設定されます。 ディクショナリの更新(最初の使用での読み込みを除く)はクエリをブロックしません。更新中、クエリは古いバージョンのディクショナリを使用します。更新中にエラーが発生した場合、エラーはサーバーログに記録され、クエリは古いバージョンのディクショナリを使用して続行します。

ジオベースを用いてディクショナリを定期的に更新することが推奨されます。更新中、新しいファイルを生成し、それらを別の場所に書き込みます。すべて準備が整ったら、サーバーで使用されるファイルに名前を変更します。

OS識別子や検索エンジンの操作のための関数もありますが、使用すべきでありません。