WeOLAP:微信 OLAP 湖倉(cāng)新場(chǎng)景優(yōu)化實(shí)踐
ClickHouse 在微信有著廣泛應(yīng)用,如何保障其自身查詢性能,并能在新場(chǎng)景中結(jié)合應(yīng)用成為了關(guān)鍵問題?;谠摫尘埃_發(fā)團(tuán)隊(duì)首先針對(duì)ClickHouse 的性能問題,開發(fā)了相應(yīng)的性能觀測(cè)工具,并在數(shù)據(jù)查詢、策略實(shí)驗(yàn)等場(chǎng)景針對(duì)性進(jìn)行了湖倉(cāng)讀取、bitmap 計(jì)算等方面的探索優(yōu)化,最后將 ClickHouse 在 AI 場(chǎng)景進(jìn)行落地應(yīng)用,沉淀了融合 OLAP 能力的成熟數(shù)據(jù)管線。
一、ClickHouse 在微信的應(yīng)用
1. ClickHouse 在微信的應(yīng)用
ClickHouse 在微信團(tuán)隊(duì)有著廣泛應(yīng)用,如實(shí)時(shí)報(bào)表、AB 實(shí)驗(yàn)和實(shí)時(shí)計(jì)算等,通過將 Hadoop 相關(guān)生態(tài)集成到 ClickHouse 中,性能得到了十倍到百倍的提升,能夠做到萬億級(jí)數(shù)倉(cāng)、亞秒級(jí)響應(yīng)和穩(wěn)定高可用。
ClickHouse 在微信的集群規(guī)模有數(shù)千臺(tái),Top50 響應(yīng)時(shí)長(zhǎng)約 0.34 秒,平均響應(yīng)時(shí)長(zhǎng)為 4 秒,查詢量級(jí)為每天百萬級(jí)。當(dāng)前的主要版本是基于社區(qū)的 22.8,少量版本對(duì)應(yīng)社區(qū) 23.3。
2. 新場(chǎng)景應(yīng)用
過去一年我們探索了 ClickHouse 的一些新的應(yīng)用場(chǎng)景。在湖倉(cāng)讀取方面,基于 Iceberg/Hive 進(jìn)行讀取和湖上數(shù)據(jù)加工,來緩解數(shù)據(jù)孤島問題;在實(shí)驗(yàn)新場(chǎng)景上,進(jìn)行畫像分析、人群圈選,支撐實(shí)時(shí)可見的在線實(shí)驗(yàn)系統(tǒng);另外,也與 AI 進(jìn)行結(jié)合,通過成熟的 OLAP 數(shù)據(jù)管線為近/離線模型推理進(jìn)行提效。
二、ClickHouse 的性能觀察工具
作為一個(gè)用戶,需要感知查詢的資源消耗;作為一個(gè)運(yùn)維同學(xué),需要知道如何優(yōu)化集群負(fù)載;作為一個(gè)開發(fā)同學(xué),需要快速定位慢查詢的原因。這些都離不開性能觀察工具。ClickHouse 提供了一系列便捷的性能觀測(cè)工具,如 Query Log、Query Thread Log、Sampling Query Profiler 和 Flame Graph 等。
首先是最常用的 Query Log 和 Query Thread Log,通過查詢的 query id,可以對(duì)這條查詢性能進(jìn)行觀察分析。我們還可以在代碼中增加自定義的 Profile Event,方便定制一些觀測(cè)指標(biāo)。
第二個(gè)是 Sampling Query Profiler 和 ClickHouse Flame Graph,通過可視化的火焰圖能夠直觀地對(duì)內(nèi)存和 CPU 進(jìn)行分析,在 CH 可以對(duì)指定查詢進(jìn)行 profile,支持的最細(xì)粒度為 query 級(jí)別。它有一個(gè)缺點(diǎn),會(huì)將一個(gè)查詢涉及到的多個(gè)線程匯聚到一起,導(dǎo)致無法對(duì)單個(gè)線程的情況進(jìn)行分析。我們針對(duì)這個(gè)問題也做了改進(jìn)優(yōu)化,使它能支持線程級(jí)的單個(gè)展示和查詢聚合。
第三個(gè)是 Processors Profile Log,它可以幫助我們清晰地看到每個(gè)算子的耗時(shí),判斷算子間是否均衡、是否存在傾斜情況,也可以幫助我們看到算子間的依賴關(guān)系。
WeOLAP 團(tuán)隊(duì)還自研了性能分析工具 Profile Engine,從事前和事后兩個(gè)場(chǎng)景進(jìn)行優(yōu)化。在事前對(duì)用戶提交的 SQL 結(jié)合集群信息和表信息進(jìn)行分析,并基于索引、分區(qū)等給出相應(yīng)可視化改進(jìn)建議;在事后基于制定的規(guī)則對(duì)大查詢和慢查詢進(jìn)行分析,給出優(yōu)化建議。通過這個(gè)工具,既可以給使用者提出優(yōu)化建議,也可以幫助使用者平衡集群負(fù)載。該工具上線后的使用效果很不錯(cuò)。
三、湖倉(cāng)讀取優(yōu)化
ClickHouse 在湖倉(cāng)鏈路中既是存儲(chǔ)組件又是計(jì)算組件,跨層的存在會(huì)導(dǎo)致一些問題:
- ClickHouse 中的數(shù)據(jù)有孤島化傾向,不能被 Spark、Presto 等引擎查詢。
- 數(shù)據(jù)冗余,Shared-nothing 帶來昂貴的機(jī)器成本。
- 繁瑣的數(shù)據(jù) ETL。
我們的改進(jìn)目標(biāo)是讓 ClickHouse 作為計(jì)算組件,直接讀取湖倉(cāng)數(shù)據(jù)。
其中存在一些挑戰(zhàn):
- ClickHouse 目前只支持單機(jī)讀取 Hive。
- ClickHouse 支持讀取 Iceberg,但僅限 S3 存儲(chǔ)。
- Iceberg 沒有 C++ 的 API。
- 現(xiàn)在只支持 Hive/Iceberg 外表,一旦表 schema 變化,需要手動(dòng)同步 DDL 修改。
- 部分場(chǎng)景的 ORC 讀取性能不佳。
針對(duì)上述問題,我們采取了如下優(yōu)化措施:
- 新增外置 HTTP 協(xié)議的 Iceberg API server,使用 Java 繞開 C++ 限制,實(shí)現(xiàn)外置 server。
- 通過一致性 hash 分發(fā)文件路徑到各節(jié)點(diǎn)實(shí)現(xiàn)分布式讀取。
- 對(duì)元信息和數(shù)據(jù)文件進(jìn)行 cache。
- 讀取集群和計(jì)算集群分離。
增加外庫(kù)實(shí)現(xiàn),避免手動(dòng)繁瑣的建表和元信息不一致問題。
ClickHouse 在讀取某些 ORC 文件時(shí)會(huì)很慢,例如示例的 select * 和 select count(1)。
通過火焰圖分析,我們發(fā)現(xiàn) Apache Arrow 庫(kù)讀取 ORC 有大量的 memcpy,十分影響性能。我們切換到了 Apache ORC 庫(kù)進(jìn)行讀取,整體性能提升了 0.5 到 1 倍。
在某些場(chǎng)景會(huì)出現(xiàn) IO 浪費(fèi),如圖中的 select 一列,在 stripe size 為 4MB 和 64MB 時(shí),對(duì)應(yīng)解壓后的大小相等,但 HDFS 讀取量差異很大。
ReadBuffer 在讀取時(shí)很容易 cache 大量我們不需要的數(shù)據(jù),幫我們緩存很多不需要的列,造成大量 IO 浪費(fèi)。此外,在讀取時(shí)會(huì)先讀 stripe footer,再讀 row data,導(dǎo)致頻繁地 HDFS seek。以上這兩點(diǎn)是造成 IO 浪費(fèi)的主要原因。
我們采用 IO 預(yù)讀機(jī)制對(duì) ORC 的讀取性能進(jìn)行優(yōu)化。首先,ORC 文件可以提前計(jì)算文件中哪些 range 是需要被讀取的,基于此,我們將讀取規(guī)則改為當(dāng)讀命中某個(gè) range 時(shí),按照 range 粒度執(zhí)行預(yù)讀,并將臨近 range 進(jìn)行合并,減少HDFS seek 次數(shù)。
在應(yīng)用該讀取優(yōu)化后,性能提升十分明顯,以圖中的讀取 6 列為例,原有的 40 秒查詢縮短至 3.7 秒,提升了 10 倍。
此外,我們還做了 HDFS 優(yōu)化、元信息優(yōu)化和資源并發(fā)鏈接限制,基于這些優(yōu)化,在典型場(chǎng)景性能提升了 5 到 10 倍。
四、實(shí)驗(yàn)場(chǎng)景 Bitmap 優(yōu)化
在命中分析、畫像圈選中可以使用 bitmap 進(jìn)行查詢加速,將原有的交并補(bǔ)邏輯轉(zhuǎn)換為位圖操作,相比明細(xì)表的聚合或 join 查詢,通??梢匀〉脭?shù)倍的性能提升。
ClickHouse 數(shù)據(jù)按行進(jìn)行拆分運(yùn)算,在 bitmap 場(chǎng)景中,不用批數(shù)據(jù)的行數(shù),即使行數(shù)相同,其代表的計(jì)算工作量也存在很大差異,造成了數(shù)據(jù)傾斜,其中某個(gè) pipe 的工作量顯著高于別的 pipe,以至拖慢了整個(gè)查詢。
我們的解決方案是在執(zhí)行引擎新增 repartition 階段,重新進(jìn)行數(shù)據(jù)均衡,并將數(shù)據(jù)分發(fā)到所有后續(xù) pipe。在大 bitmap 計(jì)算中,數(shù)據(jù)傾斜場(chǎng)景性能提升約 10%~20%。
我們通過 ClickHouse Flame Graph 對(duì)三個(gè)線程的執(zhí)行過程進(jìn)行分析,發(fā)現(xiàn)有兩個(gè)執(zhí)行線程長(zhǎng)時(shí)間等待,而另一個(gè)執(zhí)行線程耗時(shí)在讀取 bitmap,讀取開銷遠(yuǎn)大于計(jì)算。
ClickHouse 在 mark 級(jí)以下沒有任何并行化機(jī)制,我們針對(duì)性優(yōu)化成支持行級(jí)并行讀取,對(duì)于大 bitmap 異步進(jìn)行反序列化讀取,并減少內(nèi)存拷貝操作。
另外,我們通過對(duì)原有字段編碼進(jìn)行壓實(shí),既節(jié)省了存儲(chǔ)空間,又提升了性能。
新增內(nèi)核特性可編碼字典 Encode Dictionary,支持單機(jī)字典和副本同步字典,支持所有原生 ClickHouse 字典函數(shù),支持 value to key 反查,以及 bitmap to bitmap 編碼。
在經(jīng)過以上優(yōu)化后,我們?cè)跍y(cè)試數(shù)據(jù)集上的性能提升很明顯。在 bitmap32 上,求并集和交集有 10 倍的性能提升,在 bitmap64 上,有百倍的提升。
在實(shí)際業(yè)務(wù)應(yīng)用上,bitmap64 場(chǎng)景從查不了變?yōu)椴榈每?,bitmap32 場(chǎng)景從快到更快,在畫像分析、實(shí)驗(yàn)留存分析和表存儲(chǔ)等方面優(yōu)化效果都很不錯(cuò)。
五、ClickHouse with AI
隨著機(jī)器學(xué)習(xí)的興起,圖片或文本通過 embedding 高維向量的方式表達(dá),求解相似度會(huì)轉(zhuǎn)換為計(jì)算向量間的距離。在離線加工場(chǎng)景使用 OLAP 有很多優(yōu)勢(shì),比如可以基于元數(shù)據(jù)過濾、做一些聚合操作,以及配合 UDF 進(jìn)行加工等等。此外,我們也在精確距離運(yùn)算、ANN 索引等方面做了一些探索性的優(yōu)化。
我們基于 ClickHouse 對(duì)整套算法鏈路做了重構(gòu),融合 OLAP 成熟數(shù)據(jù)管線,實(shí)現(xiàn)了推理、加工和檢索一體化。當(dāng)有復(fù)用需求時(shí),可以直接修改數(shù)據(jù)管線中的 SQL 配置或 UDF,從而大大降低了使用成本。
我們還做了向量精確檢索查詢優(yōu)化,將其封裝為 SQL,對(duì)于后續(xù)的需求可以方便地進(jìn)行修改迭代。并且對(duì)查詢 SQL 進(jìn)行了性能優(yōu)化:
- 通過 SQL 改寫,采用 with 代替 join,減少冗余計(jì)算;prefilter 提前過濾不必要元素。
- 使用 ZSTD 壓縮,優(yōu)化數(shù)據(jù)結(jié)構(gòu)。
- 加入 repartition 階段,解決線程間數(shù)據(jù)傾斜問題。
另外,我們還優(yōu)化了 embedding 計(jì)算相關(guān)函數(shù),在業(yè)務(wù)場(chǎng)景中取得了 4 倍的性能提升:
- 我們?cè)趦?nèi)核中新增了一個(gè)向量距離計(jì)算函數(shù) NormalizedCosineDistance,它可以在歸一化場(chǎng)景下減少整體計(jì)算量。
- 同時(shí)我們也根據(jù)業(yè)務(wù)場(chǎng)景定制了 embedding vector distance 函數(shù),通過大幅減少計(jì)算的過程中的 memcpy,性能有了很大的提升。
以上就是本次分享的內(nèi)容,謝謝大家。