Map類型:ClickHouse中對動態字段的支持 背景介紹 Map類型使用示例 Map類型的取值實現 Map類型的跳數索引 Map相關函數

背景介紹

在交互式分析場景下,很多時候除了固定字段之外,還會有一些動態字段的需求。比如,在遊戲場景下,需要動態存儲用戶每個遊戲的play時長。

這種場景下,我們希望在一張表中同時存儲固定字段和動態字段的信息,並且可以高效地使用動態字段做過濾查詢。

Map類型使用示例

CREATETABLEuser_game_play

(

    mid UInt64,

   buvidString,

    game_play_durationMap(String, UInt32),

   log_dateString

)

ENGINE=MergeTree()

PARTITION BY log_date

ORDERBYmid; 

insert into user_game_play values (1, '123', map('王者榮耀',3600, 'FGO', 1800), '2021-11-14');

SELECT game_play_duration['王者榮耀'] AS duration FROM test.user_game_play

┌─duration─┐

│    3600 │

└──────────┘

Map類型的取值實現

ClickHouse從v21.1.2.15-stable版本開始支持Map類型(詳見PR#1586),其讀取key對應value的實現邏輯大致如下:

1. 內部用兩個數組(ColumnArray)分別存儲key和value值,我們分別稱之爲 key_array和value_array。

2. 對於Map的取值操作(即map[‘key’]操作),先在從key_array中找到要找的key的下標,然後根據這個下標到value_array裏獲取對應的值。

具體實現細節詳見源碼FunctionArrayElement::executeMap.

從上述分析可知,Map類型的工作方式本質上和用兩個數組分別存儲key和value的方式是一樣的。只是在功能上做了封裝,提高了用戶使用的便捷性,但在性能上並沒有變化。

Map類型的跳數索引

索引類型

爲了提升map操作的性能,我們在社區版本的Map類型基礎上,給其加上了多種類型的skipping

index,包括 bloom_filter ,tokenbf_v1,ngrambf_v1

上面這三種skipping index本質上都是用bloom filter存儲每個索引粒度的索引值。其中,tokenbf_v1和ngrambf_v1只支持String類型,bloom_filter可支持各種類型。

1. ngrambf_v1是對字符串中固定長度的substring做bloom filter存儲和檢索。

2. tokenbf_v1是對由非字母數字符號分隔開的token做bloom filter存儲和檢索。

3. bloom_filter則是直接對字段取值做bloom filter存儲和檢索。

Map類型的跳數邏輯

在數據寫入到Map類型字段時,所有的key會被抽取出來生成每個索引粒度對應的bloom filter。

對於針對Map類型字段的過濾條件,如:

    where game_play_duration[‘王者榮耀’] >= 1800 and game_play_duration[‘王者榮耀’] <=3600

會做以下處理:

1. 從filtering condition中提取map的key。

2. 分析過濾操作符(如 = , >=, <=, >, <, like , in , not in),如果該過濾條件在map不包含對應key時不可能成立,則利用bloom filter過濾掉不可能包含對應key的數據塊(索引粒度)。

具體實現細節詳見源碼PR #28634

添加索引示例

CREATETABLEuser_game_play

(

    mid UInt64,

    buvidString,

    game_play_durationMap(String, UInt32),

    log_dateString,

    Index idx game_play_duration TYPE bloom_filter GRANULARITY 2,

)

ENGINE=MergeTree()

PARTITION BY log_date

ORDERBY mid;

影響跳數效果的因素

在我們的性能測試中,給Map類型添加skipping index可以收穫的性能提升差異很大。

效果好的case可以十幾到幾十倍的性能提升,而效果不好的則沒有明顯提升。

跳數索引的過濾效果和兩個數據特性相關:

1. 索引值的cardinality:這個比較好理解,當索引值cardinality很小(比如性別,可取值只有男和女),那麼過濾效果通常有限。

2. 索引值的分佈是否聚集:ClickHouse的跳數索引和主鍵索引一樣,也是稀疏索引。當索引值分佈非常離散時,即使包含查詢值的記錄佔比很小,但可能每個數據塊(索引粒度)都包含查詢值,那麼所有數據都需要讀進內存做過濾判斷。

Map相關函數

ClickHouse社區版本中已經實現了一些map類型相關的函數,包括:

1. map : 基於傳入的鍵值對生成Map類型對象。

2. mapKeys : 獲取map對象的所有keys。

3. mapValues : 獲取map對象的所有values

4. mapContains : 檢查map對象是否包含指定的key。

更多map相關函數細節詳見這裏

另外,我們添加了兩個map函數mapContainsKeyLike和mapExtractKeyLike(已合併進社區版本,詳見這裏) 。其中mapContainsKeyLike函數支持通過tokenbf_v1索引進行跳數過濾。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章