背景:
服務器個數:ck小集群4台
單台服務器內存:256G
CPU:48核
bitmap存儲結構,一條數據大概在2M左右
表的結構如下:
CREATE TABLE yiche_index.dms_pds_user_dvid_interest_bitmap ( `dt` LowCardinality(String) COMMENT '日期', `dim_type` LowCardinality(String) COMMENT '維度類型', `dim_id` LowCardinality(String) COMMENT '維度值', `devid_bmp` AggregateFunction(groupBitmap, UInt32) COMMENT '明細' ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/yiche_index/dms_pds_user_dvid_interest_bitmap', '{replica}') PARTITION BY toYYYYMMDD(toDate(dt)) PRIMARY KEY (dim_type, dim_id) ORDER BY (dim_type, dim_id) SETTINGS index_granularity = 8192
其中的字段devid_bmp存儲的是設備ID明細;
sql如下:
select bitmapCardinality (groupBitmapOrState(devid_bmp)) from ( select devid_bmp from dms_pds_user_dvid_interest_bitmap_all prewhere dt >= '2021-01-01' and dt <= '2021-03-31' and dim_id = '奧迪' and dim_type = '3' );
當前的操作,查詢需要消耗50s+ , 在Grafana上監控發現,瓶頸在IO
分析查詢慢的原因:
首先要了解下索引的查詢過程:
假如我的bitmap表總行數是9999行 ,但是建表指定索引粒度是:index_granularity = 8192 , 那么clickhouse就會根據粒度來划分存儲區間(primary.idx)
也就是說,索引的主鍵1~8192是在一個索引區間內,其他的主鍵在另一個索引區間內
有了這個索引區間的划分之后,索引的查詢查詢過程就大致分成如下幾步:
1、where dim_id=789 會分成一個閉區間:[789 , 789]
2、使用上一步查詢的dim_id划分出的區間與生成的索引區間做交集:[789 , 789] ∩ {[1 , 8192] , [8192 , 9999]}
3、第一步交集處理后,發現索引區間的[1 , 8192]是包含[789 , 789]
到這一步就可以定位到為啥上面的sql慢了,直接背景介紹時候說過,一條bitmap數據大小是2M,但是索引區間划分是8192
也就是說我查詢一天的數據,會產生:2M*8192條記錄,這些記錄是在本地表(查詢的是分布式表),在計算的時候要產生密集的IO;
如果查詢的是3個月時間,那么產生的IO粗略看就是:2M*8192*90天
所以要優化也非常簡單:我們bitmap表的特點是:條數少,但每條都很大(2M),因此減小索引間隔粒度大小即可
比如將索引間隔粒度改變為4:SETTINGS index_granularity = 4
重寫灌入數據后再次查詢,同等條件,同等結果。本次查詢只需要1s