資料平臺架構設計中,ClickHouse如何完勝Druid?

語言: CN / TW / HK

導讀:本文的主題是基於ClickHouse的廣告資料平臺架構實踐。包括廣告業務面臨的現狀,為什麼會使用ClickHouse來提供資料多維分析服務,如何基於ClickHouse的優勢和特點在適應億貝廣告業務場景的前提下來設計系統平臺架構,實踐過程中遇到的技術問題和解決方案,以及資料平臺在日常使用、版本迭代、質量監控過程中是如何去做的。

具體將圍繞以下幾部分展開:

  • 億貝廣告業務場景介紹

  • 選擇ClickHouse

  • 億貝廣告資料平臺建設實踐

一、億貝廣告業務場景介紹

1、億貝廣告業務場景

首先和大家分享下億貝的廣告業務是什麼樣的。我們這裡討論的主要是億貝第一方賣家廣告業務場景。賣家可以與億貝合作,通過廣告的形式可以給自己商品帶來銷量,點選,從而提高他們的營業額。

億貝的廣告形式主要分為兩種,一種是基於銷量的方式,每幫助賣家成交一筆訂單,億貝都會從中收取相應的廣告費;第二種是基於點選的模式,通過智慧推薦的演算法可以將不同的商品推薦給不同的使用者,然後使用者通過點選自己感興趣的商品形成點選給賣家帶來流量,億貝就會通過這些流量向商家收取一定的廣告費用。所以說在這種場景下給商家提供一個廣告專案報表就是必不可少的。

賣家需要了解他在站點廣告投放上的表現是怎麼樣的,主要會通過點選率或者銷售率來反應,這些資料都會實時的發生在個人頁面上,可以看到用紅框標出來的就是廣告商品,當頁面上發生了廣告商品的點選或者曝光,就會伴隨一些商品資訊,通過建立的資料管道流到分析引擎,然後通過右邊這種圖展現給賣家,賣家可以對商品進行各個廣告維度的分析,從而來指定下個季度或者下個年度的廣告策略以及預算。這個就是主要的業務場景。

2、億貝廣告系統全景圖

這裡是億貝廣告系統架構的全景圖。

可以看到億貝的廣告位主要是出現在首頁,搜尋頁,商品頁面,推薦欄,移動端。 這部分商品的資料會通過離線和實時兩條鏈路最終流向流入分析引擎,所以億貝的廣告引擎也是典型的lambda架構。

為什麼會使用離線和實時兩條線呢,主要因為電商的資料在時序上會發生一些業務關係,比如一筆訂單發生幾天後產生退款,這種場景在實時資料流中是比較難處理的,我們會通過離線資料對這種情況進行修正。與此同時,我們也會通過點選對賣家進行收費,這就會產生同行之間的惡性競爭,比如說,賣家會故意點選對手賣家的商品產生不必要的廣告費用,我們的系統也會對這種行為進行監控。這種監控邏輯在實時和離線都會存在,但是有一些邏輯還需要離線情況來計算,因此也可以看到,我們是從離線資料看出最終資料一致性的標準。實時資料需要依賴隔天的離線資料進行修正,這就涉及了離線資料的替換和更新問題,這部分我會在之後詳細介紹。

資料流的計算引擎的分析之後會通過第三方API的方式提供給賣家,同時也會給站內的一些服務提供API介面,也會有UI提供給分析師團隊,給他們提供一個市場策略的資料支援。

3、面臨的挑戰

億貝的廣告場景給研發團隊帶來的主要技術挑戰有:

首先,億貝是一個國際化的電商網站,賣家是分佈在世界各地的,所以資料也要根據不同的時區展現不同的報表,這個問題在資料量大的情況下做實時分析不是很容易的,常規的解決方案會帶來比較大量的資料冗餘,我們也是通過利用ClickHouse的一些特點,解決了這個問題。

第二點是資料規模非常大,我們的規則引擎會承接億貝站上所有的流量,每天會有百億級的資料插入,這些流量如果直接接入ClickHouse分散式表的話,肯定是不行的,會對ClickHouse分散式伺服器帶來大的壓力,所以我們是通過一套自研的計算引擎來解決這個問題。

第三點是資料的實時性要求,這個問題同樣會根據資料量的增加而變得棘手。

同時由於我們採用的是離線和實時分離lambda 架構,所以離線資料更新的原子性和資料匯入的一致性也成為了要解決的問題。

最後在業務場景下,也會對資料有各種要求,各種業務要求和規則變更也會給版本的迭代帶來一些挑戰,我們也是通過一些巧妙的設計解決了這些問題。

二、選擇ClickHouse

1、ClickHouse VS Druid

基於druid做的一個分析引擎,是2012年開源的,是 Apache基金會旗下專案,它一直都是以千億級資料提供亞秒級的查詢延遲而聞名,社群比較活躍,為資料匯入提供了許多模版,同時支援內建多種資料來源。 但是在廣告場景下,基於benchmark分析來看,ClickHouse會比druid有許多可取之處。

首先是資料儲存方面,ClickHouse的資料壓縮和列式儲存會極大節省儲存空間,而druid和其他多數資料庫都是基於時序的,druid在查詢大範圍的資料時會出現效能問題,而利用ClickHouse的分割槽鍵優化可以有效的解決這個問題。

在查詢方面,druid的排序,聚合能力都不太好,靈活性和擴充套件性也不夠,比如缺少join,子查詢,主鍵排序等這些需求。而這些用SQL都可以通過ClickHouse來支援解決。同時ClickHouse也是一個MPP的計算架構,通過主鍵索引,向量化引擎處理,多處理器併發和分散式查詢這些方式,壓榨CPU的資源,在資料查詢方面具備顯著的優勢。

除此之外,運維成本也是我們主要考慮的範圍之一,Druid的運維是非常複雜的,雖然支援多種資料攝入的元件,但是元件的構成是比較複雜的,節點數量有6種之多,而ClickHouse架構採用的是對等節點的設計,節點就有一種型別,沒有主從節點,如果使用副本功能也是也是採用外部的Zookeeper來同步資料段,所以說給運維工作提供了不少便利。

在資料攝入方面,Druid通過引入實時資料的索引,把實時資料處理成一個個分段,並歸併到歷史資料,成為分段之後的資料是不能寫入的,由於併發實時處理的索引數量是有限的,所以我們就設定了三個小時的時間視窗限制索引的任務量,因此超過三個小時時間視窗的資料就沒有辦法成功寫入,同時上游資料延遲,就會造成實時資料的消費過於滯後,這部分資料在實時管道中就會缺失了,而ClickHouse就沒有這些問題,再加上自主研發的計算引擎系統,可以很大程度上提高ClickHouse的資料消費能力,最終決定把分析引擎從Druid切換到ClickHouse。

這個是ClickHouse和Druid離線資料攝入的時間消耗圖對比圖。 我們是按天來做資料的攝入,每天有百億級的資料量,可以看到,ClickHouse的資料攝入量時間還是比較穩定的,每天在50分鐘就可以完成。 而Druid需要更長的時間來完成,它還會受到叢集的計算資源的影響,最長可達數十個小時,這對於批量的資料修復和匯入工作也會帶來一些困難。

ClickHouse之所以做到這麼快,是因為我們把資料的聚合計算和分片邏輯都搬到了Spark上完成,這就降低了ClickHouse在資料攝入過程中的計算資源上的開銷,同時也利用了Spark的並行處理能力,這部分我會在之後詳細介紹。同時利用Spark處理的中間結果,我們可以做一個數據質量監控和資料回滾的重要標準,所以說這種設計的方案也算是一個一舉兩得的方案,資料攝入的時間顯著降低給平臺的升級和架構都帶來了很多便利。雖然ClickHouse可以支援分割槽的API的操作,通過touch或者detouch的操作完全資料遷移,但是它不能利用資料分割槽和分片邏輯發生重構的場景下,因此我們自研的這個架構可以承擔起資料架構升級和資料遷移的一些重任。以上這些好處都是我們採用ClickHouse之後能夠獲得的。

2、表引擎與主鍵設計

我們看一下在億貝的場景下采用了ClickHouse的哪些儲存引擎可以最大限度發揮它的優勢。

由於我們要處理的使用者行為資料量非常大,同時這些行為資料通常是在不同維度的聚合表現在報表上,這種聚合主要是一種相加的合併聚合,除此之外為了保證資料的高可用,我們也使用了副本的功能,因此我們使用了ClickHouse的複製彙總合併樹來完成這樣的儲存。 我們實時管道會將明細資料插入ClickHouse,然後叢集的後臺任務會自動將排序鍵相同並且位於同一分割槽的資料聚合起來,雖然聚合過程是非同步完成的,但是不會影響到我們的查詢結果,這樣的資料預聚合也極大節省了資料儲存量,也節省了儲存空間。

一般情況下,ClickHouse表的主鍵和排序鍵是相同的,但是採用了彙總合併樹引擎之後我們可以單獨的指定主鍵,這種場景下我們建議刪除不需要排序或者索引功能的維度從主鍵當中剔除出去,因為主鍵執行的時候需要全部載入到記憶體中,儘量減小主鍵大小,也可以很大程度上提升查詢的效率。

我們知道ClickHouse是以檔案塊作為一個單元儲存的,從右圖可以看出ClickHouse是通過分割槽管理檔案塊,索引的過程也是通過分割槽鍵先篩選出目標分割槽,再通過主鍵和排序鍵建立序數索引,快速定位到查詢語句所要查詢的資料段,ClickHouse用排序鍵索引來進行跳躍的掃描,所以就建議在建表的時候,把記錄在業務生命週期中不變的欄位放在排序鍵,通常是distinct(count())的資料量越小,放的越靠前,這對查詢有更好的幫助。

3、壓縮與低基 

除了結合業務場景選用ClickHouse的合併樹家族和主鍵優化之外,我們還對ClickHouse其他一些壓縮演算法的特性進行了探索。

這些壓縮演算法都能顯著地減少源資料壓縮儲存量,這也是ClickHouse列式儲存的一大優勢,降低查詢IO,列式儲存也在資料查詢中的不同列選擇的壓縮演算法等級,可以在資料壓縮和資料查詢效率做一個平衡。

ClickHouse預設選擇的LZ4的壓縮演算法,除此之外,一般資料列可以選一些壓縮率高的演算法,比如ZSDT,LZ4HC,對於類似時間序列單調增長的資料可以選用DoubleDelta, Gorilla這種特殊的壓縮演算法。在生產資料集上,我們的ZSDT演算法對於字串型別資料壓縮效果比較顯著,LZ4和LZ4HC是對非字串型別資料壓縮效果比較好,更高的壓縮率意味著更小的儲存空間,這樣可以提高查詢的效能,但是對CPU的開銷也是很大的,所以資料插入的效能就會受到影響,我們在生產上測試使用了LZ4HC等級6的壓縮演算法,可以節省30%的儲存資源,但是這會對實時資料攝入的效率產生60%左右的降低,所以也需要根據情況選擇合適的壓縮演算法。

對於基數比較低的列,尤其是一些列舉列,可以採用Click House的LowCardinality來降低資料的儲存,從而降低整體資料的儲存,如果採用壓縮演算法LowCardinality可以將資料空間縮小25%,這也是我們生產資料的測試結果。我們也做過極限測試,使用最大壓縮率,再配LowCardinality這種方式,可以將原來資料壓縮到整體的13%左右。

4、商品資訊表的實時更新與連線

有些場景需要結合商品的詳情給賣家展示投放廣告的表現,這些場景主要包括比如賣家想要獲得某種商品的表現如何,某個價格區間的商品表現如何,這就需要我們對使用者的行為資料結合商品的資訊篩選和排序。

在資料分析領域通常會使用寬表的形式來解決這個問題,但是寬錶帶來的資料膨脹問題也是比較明顯的,相比於商品資訊的變更,使用者行為資料的變更會更加頻繁,這會帶來非常大的資料量的冗餘。除此之外新增商品資訊進入使用者行為資料的話,也會給整個系統帶來各個方面的依賴,同時附帶在使用者行為資料的商品狀態是行為發生時候的狀態,並不能滿足我們根據商品的現狀來產生報表的需求,所以我們最終也是放棄了這種大寬表的解決方案,用維護一張使用者資訊表來完成需求。

億貝會持續的採集商品的最新狀態,這就涉及到資料的更新問題,Click House在計算更新方面做的並不好,同時也不支援事務,所以我們就另闢蹊徑,用ClickHouse中的合併家族樹中的替換合併樹來完成更新的操作,它可以用預先設定好的聚合條件,將排序鍵相同的記錄聚合起來,我們設定了用專門的一列來記錄記錄的時間戳,聚合規則選擇時間戳最新的記錄保留下來,這樣僅通過插入的方式就可以完成資料的更新。

還有個小問題,我們無法保證它已經完成更新了,因為操作都是在後臺的任務中完成的,那麼同一時刻可能存在排序鍵相同的兩條記錄,針對這個問題我們也是採用了客戶端的聚合函式argmax,它就好比是替換合併樹中客戶端的實現,能夠將未合併的資料進行合併,保證我們查詢商品記錄的一致性。除此之外,我們也會週期性的採取OPTIMIZE操作,確保ClickHouse能儘可能多的完成資料的合併。

還有一種方案是使用聚合合併樹,同樣也可以實現資料的更新,它的實現就複雜一些,它要求客戶端在資料插入之前能知道當前的狀態,因為它的更新操作是通過刪除和新增完成的,因此在更新之前,需要知道是新增新記錄還是刪除老記錄的前提下,採取新增新記錄,所以這種成本是比較高的,我們最終也沒有選擇這種聚合合併樹的實現方式。

5、廣告資料架構

我們再來看一下資料是如何分片的。 上圖可以看到,我們是對資料進行了水平方向和垂直方向的切分,在水平方向是將資料根據天進行分割槽,以小時為粒度進行聚合,這就使我們可以支援不同時區的資料查詢場景,按天分割槽可以快速的定位到資料的查詢分割槽,這樣就能節省不少計算資源,在豎直方向上我們對資料進行了業務邏輯的分片,因為我們的查詢大多是依據賣家的表現進行查詢的,將資料根據賣家ID進行分片,不同賣家的資料會落到不同的分片上,這樣一來對於充分利用分散式叢集資源來提供服務是比較有利的,除此之外,我們的點選表,銷量表,展示表也是使用的相同的分割槽鍵,這樣就可以將表之間的join操作分配的本地伺服器去完成,節省了網路資源,也可以提升查詢效率。

6、基於廣告業務場景的儲存優化

我們的資料架構也是經過了踩坑和更新迭代的過程,儲存方面我們是採用了商家ID和賣家ID進行資料分片,主要是為了避免查詢過程中的資料熱點問題,將查詢分配到更多的機器上去完成,這就會有一個問題,高的QPS就會使得資料路由到多個節點上去計算,這就會造成大量的網路開銷,分散式節點上的計算資源也會很快耗盡,本地節點的計算資源又沒有得到充分的利用,這樣導致QPS在幾百左右就達到了瓶頸。 所以我們要將幾個請求集中到少數的伺服器上進行計算,從而降低網路開銷,充分利用副本節點的資源。

第一套方案是單純基於賣家ID進行資料分片,賣家的查詢只會落到一個分片上去計算,QPS得到了顯著的提高,這樣就很容易到了數千的QPS,因此會帶來資料熱點問題,對於商品特別多的賣家,比如售賣圖書類的賣家,它有上百萬的商品(圖書)落到節點上會給叢集帶來熱點的壓力,雖然QPS高,但是這種極端情況下的延遲高。

我們又探討了折中方案,將賣家的資料在時間維度上進行Hash,建立了一個用於分片的輔助列,用來存放行為資料的年周,年周可能是0-53的資料,將賣家的資料平均分配到53個分片上去,這樣就避免了對整體叢集的查詢,同時也避免了資料熱點的問題,相當於是在QPS和延遲兩個方面做了權衡,QPS也是達到了數千的要求。

三、億貝廣告資料平臺建設實踐

1、系統架構

上圖是廣告資料平臺系統架構的概覽。 可以看到我們使用了非常多的大資料技術棧。

離線部分主要是基於Hadoop生態。離線明細資料會在T+1天的時間就緒,我們用Spark定義了通用資料處理任務,通用任務是可以配置的,這個配置主要是建立HDFS上的欄位和ClickHouse表中的欄位的關係,當有新的click表需要接入的時候,可以通過配置檔案的方式,儘快完成上線。

ETL的任務排程我們採用了Spring batch的技術棧,它具備成熟的任務排程面板可以通過UI配置日常任務和資料的修正任務。在Spark任務提交方面我們是採用了LIVY伺服器,可以提高job排程管理的效率,也是提供Spark叢集互動的API介面,我們能靈活的監控批處理觸發的任務, 一般也會有一個Spark Cluster的解決方案, 這種解決方案對系統的耦合度會比較高,像這種方案就可以單獨把LIVY Server分離出來,可以服務於其它平臺的任務排程需求。

在線上資料方面,我們採用的是Kafka生態下的技術棧,通過Flink消費上游kafka Topic中的事件進行資料裝配,再通過定製的ClickHouse的JDBC再將資料插入ClickHouse。

在離線和實時部分都沒有采用ClickHouse原生的JDBC,主要也是為了提高資料的攝入和穩定性,這部分我們也會在之後詳細介紹。這裡的技術棧主要是使用了kafka和airflow,kafka主要用於實時資料攝入過程中的緩衝和預計算,airflow主要負責離線資料攝入的排程,它以clickhouse的副本節點為單元控制資料攝入的進度,這裡需要強調的是,我們把離線資料攝入的排程。業務方面的排程,實際的ClickHouse的資料攝入任務排程解耦開來,主要也是提供一個平臺化的服務,這樣ClickHouse可以承接除了廣告需求之外的其他分析需求的資料攝入,那麼我們的資料排程,業務也可以排程一些任務去完成除了ClickHouse之外其他儲存的資料攝入的需求。

2、實時資料寫入 

這裡我們再來介紹一下實時資料攝入是如何實現的。

資料攝入是kafka搭建的外部計算系統。 首先將資料按業務進行分片,由kafka直接寫入ClickHouse叢集的各個本地表,一個分片上的資料對應kafka的一個主題,為了保證足夠的資料吞吐量,kafka每個主題的分割槽個數都要大於ClickHouse的副本個數,這樣做的目的是可以保證,ClickHouse的副本可以並行的形式消費同一主題下的記錄,如果kafka的節點或者ClickHouse副本節點產生宕機,kafka叢集可以通過rebalance的形式保證系統的高可用,這種方案具備更好地寫入效能,因為每個分片的資料都是被並行點對點寫入的,將資料吞吐量也是均攤到所有ClickHouse節點上,給我們的系統擴容也帶來的極大的靈活性,通過目前的方案我們實現了百萬級每秒的資料吞吐量,跟源系統的延時也是控制到秒級。

3、離線替換系統架構

上圖是我們離線資料攝入的架構圖。 我們的批處理任務分為兩種型別,一種是日常的資料攝入,一種是歷史資料的修正,包括資料遷移。 使用者可以通過批處理控制不同的優先順序批處理任務,然後定時的程序會拉起優先順序較高的批處理任務優先處理。

離線資料攝入的第一步是鎖定ClickHouse的space, 在這一階段click的space確認不會發生表結構或資料分佈的變化,那這一操作不會影響實時資料,也不會影響查詢。隨後批處理任務排程會獲取click space的分片資訊,並通過LIVY伺服器傳給ETL任務,然後成功觸發ETL任務並且來監控它的狀態。Spark任務在這裡的作用主要是預分片和預聚合,它會根據預分配好的任務在Spark任務中完成不同維度的資料聚合,並將資料根據分片的資訊寫到對應的檔案目錄。從右下角這幅圖可以看出,每個檔案目錄對應幾個分片,每個任務會生成檔案的詳情,就是包含的具體的分片資訊,或者對應這個檔案下哪些資料,這些資料的一些詳情,這些會通過後續的資料匯入過程提交的資料攝入任務排程。資料攝入排程會根據這個詳情把這些資料攝入任務分配給ClickHouse的每個副本節點,副本節點在接收到任務後,會到指定目錄下拉取檔案到本地,再通過ClickHouse的client匯入資料。

我們會將每個分片的資料按照副本的個數進行倍數的均分,使得每個節點都能獲得相同數量的匯入任務,通過這種方式我們也是將離線資料的攝入吞吐量與系統架構解耦,充分利用Spark並行處理的優勢,同時能夠通過擴充副本的數量來靈活控制資料攝入的速率。在完成了資料攝入步驟之後,資料攝入任務排程會按照節點來完成資料質量驗證,這一環節也是通過比對資料匯入之前的輸出詳情與實際匯入的結果完成的,所以我們的離線資料攝入分為兩個環節,一個是Spark job的預處理環節,還有一個是ClickHouse的資料攝入,使用者可以通過各個帶服務的UI來監控任務的進度。

4、分割槽資料替換

現在講一下離線資料更新是如何保證大規模的資料一致性的。 離線資料和實時資料相比會有一天的延時,使用者看到最近一天的資料都是實時資料。 之前資料架構環節我們提到資料的分割槽是按天為粒度來進行分割槽的,因此這種方式也是將實時資料和離線資料隔離開來了。

這部分離線資料替換主要是發生副本節點通過ClickHouse的client的資料匯入階段,副本節點首先會將HDFS上的檔案下載到本地節點,隨後在本地建立一個ClickHouse的臨時表,將資料通過ClickHouse的client匯入到臨時表中,再通過分割槽的API atouch的detouch將臨時表的分割槽新增進主表,所以我們是叫資料替換是因為我們完成的是分割槽的替換,而不是資料記錄的更新。

在這個過程中匯入的離線資料對使用者目前是不可見的,因為我們是通過版本控制來完成資料可見性的控制,通過上面的過程,我們就完成了離線資料的匯入。資料匯入過程本身不會花費太長時間,因為並行度比較大,主要的時間消耗是逐表資料的驗證上,當資料出現質量問題,我們也會通過重試來完成資料的匯入。

5、資料替換的原子性 

我們再來看一下不同版本的資料對於使用者的可見是如何實現的。 因為我們這裡處理的是線上系統,ClickHouse又是一個叢集的部署方式,這就使得我們不能保證同一時間完成所有節點的資料替換。 對於使用者而言,是沒有辦法接受查詢結果是不斷變的,因此我們就需要做到資料替換過程的原子性。 剛才已經介紹了我們如何使用ClickHouse的分割槽資料來進行插入的,對於資料的可見性,我們使用了ClickHouse的記憶體字典。

字典中包含了兩個列,分別是A和B,它們記錄了最近兩次資料替換之後的資料版本,我們把它稱做版本列,另外一列叫active列,它記錄了每個分割槽當前活躍版本所在的版本列。那這樣每次我們進行資料替換,資料的版本號替換到非活躍版本列,原子操作完成的主要方式就是將active列指向非活躍的版本列,這樣就可以實現資料離線替換的原子性,同時提供了資料版本回退的能力。

因為我們有不同版本的資料,一旦線上的監控系統發現有資料問題,也可以在很短的時間回退到上一版本。這裡的資料版本是作為一個特殊的列存在於每個資料表中的,查詢的時候會指定版本號,可以通過dictGetOrDefault的方式來實現,用ClickHouse的條件優化關鍵詞PREWHERE預先指定版本。這樣在查詢結果中,就可以只包含活躍的資料。

大家可能會注意到我們這種離線資料替換方式會帶來一個問題,如果持續替換一個數據分割槽會引入許多無用的分割槽資料。在查詢過程中,首先會通過PREWHERE掃描活躍的版本,這就意味著版本數越多,掃描時間越長。我們也是設定了一個定時的任務,來定期清理不需要的資料版本。在查詢語句上也做了優化,之前是在字典上維護活躍版本的,所查詢的資料需要下發到各個節點上才能查到當前資料的活躍版本,後來我們是將字典遷移到分散式資料表中,查詢的時候就可以在分散式表中率先獲得版本號,再通過GLOBAL IN這種方式下將分割槽鍵下發到各個節點。

這樣做的好處就是利用ClickHouse的分割槽鍵索引可以跳過以往的資料版本,直接定位到最新的資料版本,也是通過這種方式,我們的查詢延遲也是得到了進一步的優化。

6、Zookeeper壓力問題與解決方案

由於離線資料和實時資料的攝入都是通過本地表完成的,那資料的全域性同步和複製任務主要由Zookeeper來完成,在資料攝入體量很大的情況下,就給Zookeeper帶來了巨大的壓力。 ClickHouse不是一個完全的分散式系統,同時也不是一個典型的MPP架構,它與傳統的MPP架構相比缺少了全域性的元資料管理,如果想擴充套件任務到多個節點,還需要藉助外部元件的一些協同。 ClickHouse的協同能力是比較弱的,沒有中心節點就會導致很多問題,比如資料的同步和複製都是依賴外部的Zookeeper叢集來完成的。

我們在第一版本中也遇到了由於Zookeeper壓力太大造成的一些系統問題,從ClickHouse這端反應出來的就是與Zookeeper的會話頻繁超時,某些副本的本地表停留在只讀模式,還有就是ClickHouse的操作發生超時,從Zookeeper叢集這端觀察到的就是不斷的斷開網路連線,嘗試重連,瘋狂列印建立連線的日誌,這對資料查詢不會造成影響,但是會對實時和離線的資料攝入帶來隱患,主要體現就是離線資料替換無法成功匯入或者成功同步而失敗。

那實時資料如果無法成功插入資料,多次嘗試之後,會去消費者節點的觸發kafka分割槽的rebalance, 將當前的分割槽重新分配到ClickHouse的其它副本節點嘗試插入,這種錯誤不會經常發生,我們可以通過重試最終將資料匯入,對資料的攝入效率就帶來了很大的影響,這個問題主要原因在於Zookeeper的效能會根據客戶端的連線數的增多而極度下降,在我們的系統中會有數千個Zookeeper的客戶端連線,ClickHouse的操作需要藉助Zookeeper的協同,而Zookeeper的壓力過大就會造成操作超時,如果Zookeeper的操作超時就會造成會話超時,因為在超時之後,Zookeeper會嘗試重連ClickHouse,重建會話,這就進一步加大了Zookeeper的壓力,最後就會引發雪球效應。

解決這個問題的方案可以設定operationtimeout,儘量減少不必要的全域性操作,這些操作都可以緩解壓力,但是如果真正是一個治本的方案,我們還是要解決Zookeeper中心壓力的問題,我們將Zookeeper的叢集進行進一步拆分,不同的叢集管理不同的分片,通過這種方式降低了Zookeeper的壓力,也解決了這個問題。

7、資料替換的一致性保證

我們再來看一下系統中是如何把控資料質量的。 可以看到資料質量會在各個階段進行把控。 剛才提到資料任務排程元件會對Spark資料的輸出進行校驗,資料插入元件也會在資料替換時將臨時表和預處理的資料進行比對,資料攝入後,也會有資料質量監控平臺對線上資料各個生命週期的資料進行監控,一旦有問題也是做到及時預警,也是及時控制版本,儘量避免線上版本的資料發生錯誤的時間。

8、基於ClickHouse的廣告應用架構

上圖是一個整個的查詢架構。 我們會統一的提供資料訪問層對外提供API,資料查詢主要是分為同步和非同步兩種呼叫方式,資料引擎是ClickHouse,查詢服務層會將請求發給ClickHouse來進行查詢,我們遇到一些QPS比較高的需求或者是做一些聚合度比較高的查詢,這種需求對ClickHouse引擎不是很友好,我們也會整合一些其它的資料儲存,補全ClickHouse在服務端的一些短板,所以我們通過查詢服務層將查詢路由到適合的資料儲存引擎上來完成一次資料的查詢。

9、測試與釋出 

最後再介紹一下系統的測試和釋出。 每當有大版本資料更替的時候,會存在一個雙資料來源的階段,流量通過將老資料來源切換到新資料來源的方式來完成釋出。 拿第一次升級來舉例,在查詢應用中整合映象轉發的依賴,將生產中的查詢映象到新版本的查詢服務上,這兩個映象呼叫都會被最終返回到映象的伺服器上,通過比對映象伺服器上的結果來判斷系統的狀態,從而確保系統釋出的質量。

作者丨周路

來源丨公眾號:DataFunTalk(ID:datafuntalk)

dbaplus社群歡迎廣大技術人員投稿,投稿郵箱: [email protected]