京東OLAP億級查詢高可用實踐
OLAP(On-Line Analytical Processing)是聯(lián)機分析處理,它主要用于支持企業(yè)決策和經營管理,是許多報表、商業(yè)智能和分析系統(tǒng)的底層支撐組件,支持從海量數據中快速獲取數據指標。
京東OLAP的發(fā)展歷經Druid、Kylin、Doris和ClickHouse,廣泛服務于京東各個子集團和各類場景中,經歷了數次大促的考驗無事故,本文會重點以ClickHouse為主,介紹京東OLAP高可用實踐情況,如業(yè)務場景和選型的考量,運維部署方案,高可用架構以及在使用過程中遇到的問題和未來改進計劃。
京東數據分析的場景非常多,零售集團主營業(yè)務是在線電商業(yè)務,既有電商交易數據又有用戶流量數據,同時也有物流、健康、京喜等線下業(yè)務線。
1)交易數據的難點,就是業(yè)務復雜,需要關聯(lián)多張表,SQL中邏輯多;另外就是數據會更新,比如交易狀態(tài)和金額的變化,組織架構的變化會導致數據需要更新或刪除重新導入。
2)流量數據有幾個特點,一是只追加不修改;二是量大,因為包含用戶的點擊和瀏覽等各類行為數據,以及由此衍生的各種指標,比如UV計算;三是數據字段會經常變化。
3)實時大屏是618大促時常見一種數據展現形式,這種場景都是實時數據,一塊大屏展現數據指標比較多并發(fā)大,涉及訂單數據還需要更新,比如北京消費卷就有各種狀態(tài),可用性要求也非常高。實時寫入、數據更新、高并發(fā)以及海量數據,這些條件綜合在一起,對OLAP來說是一個不小的挑戰(zhàn)。

在大數據架構中,OLAP的上游是Spark/Flink等計算引擎,下游對接報表、分析系統(tǒng)或策略系統(tǒng)。通過實施OLAP組件,可以加快需求響應速度,簡化計算過程降低IDC成本。京東大數據發(fā)展較早,大數據體系也比較成熟,陸續(xù)引入了很多OLAP組件,主流OLAP技術都正式使用過,目前形成了ClickHouse為主Doris為輔的實施策略。
Druid是實時預聚合的技術,用于計算廣告領域比較多,面向廣告主或內部產研的廣告數據指標的計算;Kylin的技術特點是預聚合,Cube計算方式能夠加快計算速度,內置存儲邏輯,提供友好SQL查詢接口;ElasticSearch本身基于Lucence改進,用到搜索引擎的索引技術,一般用于半結構化的數據如日志或文本分析。

如上圖,選擇合適的OLAP組件,需從海量、時效性、靈活性和適應性這幾個層面來考慮。
海量則是指單一集群是否能夠支撐百億甚至千億的數據分析,是否能夠處理海量數據是衡量OLAP組件的一個基礎指標,拋開海量去談其他的沒有意義;
時效性可以提升決策的效率,能快速迭代檢驗決策效果,比如是否支持分鐘級或亞秒級端到端數據分析;
靈活性指能夠快速響應業(yè)務需求,靈活調整指標計算方式和任意維度組合分析,而不用重新部署和預計算;
適應性指能否覆蓋大部分的數據分析場景,還是只能滿足特性場景,因為靠多個組件組合去滿足需求會增加復雜度。
ClickHouse和Doris是分析型數據庫,能夠很好的滿足以上四點,也非常好適應絕大部分場景,因而成為京東內部主流的OLAP引擎,他們能組成一個互補的搭配,因為ClickHouse性能強悍,擴展性好,但使用門檻和運維成本較高,在大數據量和極限場景下使用;Doris使用簡單,運維也簡單,適用于中小數據量業(yè)務場景。
京東業(yè)務線多,數據需求旺盛,我們建立起一套小集群多租戶的模式,部署多個百臺左右的集群服務于大量業(yè)務;同時針對不同場景的特點定制化部署方案,比如存儲量大、并發(fā)大或有大查詢等不同情況。一個合理的集群規(guī)劃是實施OLAP成功的關鍵,如果剛開始方案不合適,亦或沒考慮到運維和運營成本,后面的麻煩事會接踵而來,所以我們花一些篇幅來說明這個問題。

如何配置集群和業(yè)務有多種方式,一個方案是部署一個超大的OLAP集群把所有業(yè)務放在一起,另一個方案是每個業(yè)務都獨立部署一套集群。前者業(yè)務響應速度快,資源利用率高,但業(yè)務間互相影響,大集群運維難度大;后者完全隔離,但資源無法調配,數量龐大的集群運維投入大。因此,我們選擇了分場景隔離和多租戶的模式,能一定程度上兼顧業(yè)務高可用、資源利用率和運維的方便性。
離線和實時分離,因為離線大批量數據導入,消耗CPU和磁盤,會影響實時數據的寫入;
報表和分析型分離,因為分析型有大查詢,會占用較多資源,同時幾個大查詢會把CPU打滿,影響其他業(yè)務;
高并發(fā)分離,因為高并發(fā)會大量占用集群CPU和內存,影響其他業(yè)務的查詢和寫入;低延時分離,低延時指數據需要秒級寫入,勢必會大量小文件寫入,后臺合并負擔較重,影響其他業(yè)務的寫入;
總體來說,就是回答是否實時、是否大查詢、是否高并發(fā)等問題,如果回答“是”,則需要考慮獨立一個集群來。
多租戶來服務眾多業(yè)務,因為集團內業(yè)務線眾多,為了保證服務質量,及時響應需求,提升資源利用率,我們提供了共享集群多租戶的運營方案,在一個集群中讓數十個業(yè)務一起使用,每個業(yè)務分別建立一到多個賬號,多個賬號可以是不同的團隊、產品或模塊使用,為了避免資源搶占和互相影響,多賬號通過配額來限制,也可以控制不同賬號權限比如讀、寫和DDL權限,進行分級管理。我們在CH中主要通過查詢量(并發(fā)數+Query次數)、查詢大?。▋却嫦拗?超時時間)來控制,我們統(tǒng)稱為用戶配額。
1)查詢量(并發(fā)數和Query次數):并發(fā)數指同時執(zhí)行的Query數量,設置為CPU核數的1-3倍,具體到每個賬號,可以根據在線人數峰值來預估。Query次數,在CH的Quota設置中,計數窗口是可以自定義的,為了避免峰值波動以及能快速生效,我們定義的是10秒的時間窗口,10秒內的Query數量不能超過某個限制,同時我們修改了內核對讀和寫的Query分別統(tǒng)計。
2)查詢大?。▋却嫦拗坪统瑫r時間):為了避免執(zhí)行時間過長的查詢對集群的影響,限制查詢內存大小和超時時間,單個查詢內存限制為節(jié)點內存的1/4-1/2,達到超時時間的查詢將會被終止。如果出現超過內存或時間的情況,需要降低查詢范圍以及Group By的數據量,另外看是否需要優(yōu)化存儲和SQL來提升單個查詢的性能。
通過多租戶和配額的機制,可以快速響應業(yè)務側新申請賬號或配額變更的需求,同時也對不符合最佳實踐的使用進行約束,比如慢查詢或穿透緩存的查詢。同時這個方案對獨占集群也是有效的,因為不同賬號對應的模塊之間也需要分別來控制。多租戶方案的另一個好處在出現問題時,我們可以根據賬號來縮小排查范圍,快速定位問題。
通過上云讓業(yè)務自主化使用,另一個支持業(yè)務方案是上云,通過K8S來部署集群,容器化的優(yōu)勢是標準化和資源隔離,我們通過標準化的資源搭配供用戶去選擇。我們自研了OLAP管控面,用戶可以通過管控面申請資源,配置監(jiān)控和報警以及查看查詢情況。管理員也可以進行日常運維操作,集群部署、上下線、配額調整等,當集群故障時也可通過預設方案進行自愈。
OLAP中既有存儲又有計算,是計算和存儲都密集型,資源選型和搭配合理性尤為重要。
資源類型配比要合理。不同場景資源類型的需求是不一樣的。按照我們的經驗,計算量大的業(yè)務,選擇CPU核數多主頻高的,比如分組和去重的計算;數據保留時間長的業(yè)務,磁盤空間則需要大;如果使用字典,數據需要加載到內存,則需要考慮大一點內存。一般來說CPU32核內存64-128G磁盤2-10T。
離線推薦HDD磁盤。在離線場景中,需要存儲數年的數據,存儲空間占用大,一般采用普通機械磁盤,數據在外部排序順序寫入,磁盤寫入速度和IO都能滿足要求。使用HDD磁盤時,需要堅持小批次大批量的原則,盡量降低小文件對系統(tǒng)的負擔,采用大容量的磁盤,一個好處就是可以做一些物化視圖,來提升查詢性能,以空間換時間。而實時場景,我們一般選擇SSD或NVME,隨機寫入能性能好,可以低延時高頻寫入小文件,能獲得更低的數據延時,更低的IO繁忙率。
優(yōu)先選擇單機性能高。分組或去重計算,需要把全部或部分數據匯聚到少量實例中,然后在匯聚實例中計算,依賴單節(jié)點的計算性能,集群相同核數的情況下優(yōu)先選擇CPU核數多和主頻高的,比如32核的10臺和64核的5臺,后者在某些場景下計算性能更優(yōu)。
分片和副本數量,需要根據數據規(guī)模和并發(fā)量來確定。
如何確定分片數,計算單副本未壓縮的數據量,然后除以單節(jié)點磁盤容量,除以壓縮率,就是分片數。另一個計算方式是,一個分片一天寫入條數為5億條,一是兼顧了寫入速度二是考慮到每個分區(qū)下的分片數據量不能過大。
如何確定副本數,通??梢园磫胃北?00QPS來計算(和查詢復雜度密切相關以實際壓測為準),假如有500QPS,則需要5個副本來做負載均衡;另一個考慮是,數據的可靠性,所以我們一般推薦2副本以上,避免機器故障導致數據丟失。
流行的CH部署方是單實例的,比如5分片2副本,需要10臺服務器,每臺服務器部署一個節(jié)點,如果查詢并發(fā)少,CPU和內存會有浪費。因此,我們采用多實例多副本的部署模式,如下圖4臺服務器,我們部署了4分片和2副本。

1)支持多實例:我們支持一臺物理機部署1-N個實例,每個實例對應不同的網絡端口和磁盤目錄,單實例是多實例的一種特例。
2)支持多副本:通過配置不同的副本在不同的機器上,保證了數據可靠性,如上圖,S指分片R指副本,相同分片的不同副本間互為主備。同時多副本也能充分利用多塊磁盤減輕IO壓力。
從過往的使用經驗來看,OLAP具有一定的高可用性,但是依然有一些情況會引發(fā)集群不穩(wěn)定,如數據或查詢不均衡、硬件故障等,我們一直在架構側努力去彌補這些薄弱環(huán)節(jié)。
硬件故障是無法避免的,因此如何做到在硬件故障時使用上無感知是我們努力的方向。我們部署CH集群是三層結構:域名 + CHProxy + CH節(jié)點,域名轉發(fā)請求到CHProxy,再由CHProxy根據集群節(jié)點狀態(tài)來轉發(fā)。CHProxy的引入是為了讓Query均勻分布在每個節(jié)點上,自動感知集群節(jié)點的狀態(tài)變化,我們對CHProxy做了一定的改進。當集群節(jié)點故障時,分為查詢、寫入和DDL操作來考慮如何規(guī)避其影響。

查詢時,CHProxy會轉發(fā)到健康節(jié)點,接收查詢的節(jié)點對副本有Load_balancing策略,執(zhí)行查詢計劃時會把子查詢發(fā)給健康副本,故障節(jié)點不會收到查詢請求。
寫入時,情況稍微復雜,如通過域名來寫分布式表或隨機寫本地表和查詢的機制類似。如果指定分片寫入本地表,可以在QUERY中指定分片序號,CHProxy會轉發(fā)寫入到指定分片的某個副本上,同樣會跳過故障副本節(jié)點。
DDL操作,DDL指元數據的修改,在ZooKeeper中有一個DDL隊列,如果故障節(jié)點短時間內修復后又上線了,會繼續(xù)執(zhí)行隊列中的DDL操作。如果長時間未能修復,再次上線時,會導致該節(jié)點和其他節(jié)點的元數據不一致,因此需要手工去修復。我們開發(fā)了元數據一致性檢查工具,去檢測節(jié)點元數據、ZooKeeper元數據、數據之間的一致性。
假如CHProxy故障了,域名服務有探測機制,如果請求超時或失敗不再轉發(fā)請求到故障的CHProxy節(jié)點。
而Doris在這方面要強很多,因為他有一個完善的元數據管理功能,校驗并修復元數據的一致性,有副本的校驗和均衡機制,自動修復副本故障,所以,節(jié)點故障后下線以及修復后上線,業(yè)務側幾乎感知不到。

如果是集群單節(jié)點故障,可以快速下線該節(jié)點,對業(yè)務影響較?。蝗绻捍竺娣e故障,比如機房機架故障,或需要對集群進行停機維護,對于特別重要的報表,我們有主備雙流的機房,能夠快速切換到備用集群中。
雙機房之間的數據同步,目前是通過外部雙寫雙集群的方式來處理,寫入程序需要檢查雙寫的一致性。這種方式會增加外部寫入的復雜度,同時帶來不必要的資源消耗,有較大的優(yōu)化空間。
我們有一套較為完備的故障發(fā)現和處理機制,比如定期的硬件檢測、監(jiān)控和報警系統(tǒng)和定期的冒煙測試。冒煙測試指對集群進行最常見的常規(guī)操作,比如建表、寫入數據和刪除表等操作,是集群健康度的最后一道防線。
在發(fā)現硬件故障時,能及時下線故障節(jié)點,待問題修復之后再重新上線,或者用備用機替換故障機;在程序崩潰時,能夠自動拉起等,這些操作都在我們管控面中進行,可以做到分鐘級處理。而針對這些故障,每次大促前都會進行演練以提升操作熟練度。在上層應用中,也有響應的緩存、限流和分流功能。
我們在ClickHouse的日常使用中,積累了較多的經驗也遇到不少問題,部分問題通過某些手段進行了規(guī)避和優(yōu)化,如上文的集群規(guī)劃和高可用架構,但是依然存在一些從架構層面比較難以解決的問題。
ClickHouse并發(fā)能力,因為CH是MPP架構,分布式表的查詢會分發(fā)到所有節(jié)點去執(zhí)行,每個分片的節(jié)點都會參與計算,并發(fā)能力和單機是一樣的,增加副本可以提升并發(fā)能力。另一方法是提升單個查詢的查詢性能,比如通過改寫SQL、物化視圖或者字典表的使用降低查詢時間。在查詢時間優(yōu)化到幾十毫秒以內,增加副本數可以讓QPS達到數千甚至上萬。
ClickHouse Join優(yōu)化,CH的Optimizer不夠自動化,很多SQL需要顯式的指定執(zhí)行順序和優(yōu)化參數。我們之前做過ClickHouse的TPC-DS的測試,大部分多表Join的SQL都需要改寫,比如把Join改為子查詢,改為本地表Join,設置distributed_group_by_no_merge去做分布式GroupBy等,改寫之后的性能比較好,但大表和大表的Join在右表數據量達到千萬級別之后,性能會急劇下降。
分布式的一系列問題。這個問題比較復雜,簡單來說,CH中強依賴于ZK,而ZK的性能瓶頸和不可擴展性決定了CH的使用局限,另外一方面ClickHouse中的元數據管理很松散,缺乏統(tǒng)一的完整的解決方案。
1. ClickHouse把數據同步和DDL操作放在ZK中,產生兩個問題,第一個問題是ZNode會隨著節(jié)點和數據規(guī)模擴大,ZNode達到一定數量會引發(fā)訪問ZK超時,第二個問題是沒有保證ZK和CH之間元數據的一致性,經常出現ZK的元數據、節(jié)點的元數據、節(jié)點的本地數據之間不一致。
2. 不方便擴縮容,增加和減少副本是可以的,但是擴分片需要數據均衡,比如下圖S1和S2兩個分片,增加S3這個分片,需要把S1和S2的數據分發(fā)一部分到S3,這樣就會涉及到數據在分片間移動,如何在線平滑的移動數據,是一個難解決的問題。

3. 導入事務和冪等性, ClickHouse可以支持100萬內數據導入的原子性,這批數據要么都成功,要么都失敗,但沒有保證數據一致性和持久化,比如數據寫入到某個副本中,寫入后副本數據還沒同步到其他副本,此時節(jié)點故障了,數據就丟失了。當開啟了Spark的推測執(zhí)行,或導數程序故障重啟等問題時,會導入重復數據。也就是說CH的導數并未實現Exactly-once的語義。
京東的OLAP的實踐規(guī)劃,大致上按照提升高可用性,降低使用門檻,提升需求響應速度等方面展開。
統(tǒng)一元數據管理:為了解決上面的問題,我們目前正在研究基于Raft的ZooKeeper替代方案,一方面是提升吞吐量和容量,另一方面是需要和ClickHouse結合更加緊密,保存更多元數據類型以增強CH的分布式能力,比如節(jié)點狀態(tài),元數據管理,副本、分區(qū)和文件信息,并在此基礎上形成彈性擴縮容的能力,集群遷移和備份恢復能力,以及跨數據中心數據復制能力。
管控面產品化:在使用CH和Doris的過程中,特別是大促的經歷,讓我們積累了大量的運維和故障處置腳本,我們正在把這些腳本進行產品化,讓用戶自助式使用OLAP,如資源申請,創(chuàng)建用戶和庫,自助式的監(jiān)控報警,異常處理和性能診斷,對管理員側,做到集群部署和管控,以及故障自動診斷和治愈。
云原生的OLAP:在容器化部署的同時,進一步實現云原生,利用HDFS和對象存儲的優(yōu)勢,把存儲層放到外部,避免數據的重復存儲,節(jié)省導入時間,計算節(jié)點可以彈性擴縮容。存儲分離出來之后,存儲如何擴縮容,以及計算節(jié)點和存儲分片之間如何映射,都是新的問題,這塊需要繼續(xù)研究。
其他方面如查詢優(yōu)化、分布式緩存、易用性提升等也都在規(guī)劃之中。京東數據中心的OLAP團隊,已有幾千臺服務器,覆蓋交易、流量、算法等場景,同時我們也積極參與和回饋社區(qū)
