从 ES 到 Kylin,斗鱼客户端性能分析平台进化之旅
一、背景
?
斗魚是一家面向大眾用戶的在線直播平臺,每天都有超大量的終端用戶在使用斗魚各客戶端參與線上互動。伴隨業務的迅猛發展,斗魚需要對客戶端采集到的性能數據進行統計和分析,開發出具有多維度分析圖表和數據監控的 APM (Application Performance Monitoring,應用性能監控) 平臺。
?
針對不同的客戶端采集的不同數據,我們需要將各種維度之間相互組合并聚合,最終產出的數據變成指標在圖表中展示。例如:對在時間、地域、網絡環境、客戶端以及 CDN 廠商等維度聚合下的各項指標情況進行多維度分析,包括客戶端網絡性能(包含完整請求耗時,請求耗時,響應耗時,DNS 耗時,TCP 耗時,TLS 耗時等等指標)各類錯誤時間段內的占比以及詳細數量、狀態碼分布等等。圖一和圖二分別是兩個示例:
△ 圖一
△ 圖二
?
我們最初的 APM 平臺采用了市面上非常流行的?Elasticsearch (簡稱 ES)實時聚合實現。配合自研多數據源統一接口(EST 多數據源統一接口平臺)框架,能夠實現維度指標的自由組合查詢。數據采用 storm 實時消費 kafka 寫入 ES,做到了數據的實時展示。告警采用定時查詢 ES 的方式。
?
不過運行一段時間后,我們發現基于 ES 的方案存在問題:采用 ES 實時聚合的方式,大多數時候對單個字段的聚合查詢是非常快的,一旦遇到較為復雜的多維度組合查詢并且聚合的數據量比較大(如數十億),就可能會產生大量的分組,對 ES 的性能壓力很大,查詢時間很長(幾十秒到數分鐘)導致用戶難以等待,還可能會遇到數據精度丟失的問題。因此為了支撐業務發展,考慮再三我們決定尋找替代方案,注意到 Apache Kylin 在大數據 OLAP 分析方面非常有優勢,于是決定采用?Kylin 替換 Elasticsearch, 對斗魚 APM 平臺中存在的問題進行優化。不試不知道,一試嚇一跳,效果還真的不錯。
?
二、使用 Kylin 的挑戰和解決方案
我們使用 Kylin 的過程也不是一帆風順的,期間不斷學習、摸索、踩坑,積累了一定的經驗。這里分享一下我們遇到的一些挑戰,以及找到的解決辦法。
?
1. Kylin 集群的搭建
由于是第一次使用 Kylin,而 Kylin 依賴 Hadoop,其集群的搭建便是第一道難題。作為獨立業務使用,為了保證業務的穩定性,不能依賴于公司的 Hadoop 主集群,所以需要單獨搭建一整套供 Kylin 使用的運行環境。
?
Kylin 支持主流的 Hadoop 版本。圖三是 Kylin 官網介紹的 Kylin 對各組件的版本依賴,可以看到還是比較容易滿足的。
△ 圖三
?
Hadoop 集群我們選擇了主流的 Cloudera CDH 5.14.4,Kylin 2.4.1。采用 CM 的模式搭建,目前集群共17臺機器,其中 CM 節點3臺,角色包含 HDFS,YARN,Zookeeper,Hive,HBase,Kafka(主要是消費使用),Spark 2 等。其中 4 臺機器上部署了 Kylin 服務,采用了 1 個 “all“ 節點,1 個 “job“ 節點,2個 “query“ 節點的模式,確保了查詢節點和任務節點都互有備份,滿足服務的高可用。
?
2. 構建實時 Cube 中的問題
斗魚客戶端收集到的 APM 數據會先暫存于 Kafka 消息隊列中,Kylin 支持直接從 Kafka topic 中攝入數據而不用先落 Hive,于是我們選擇了這種直連 Kafka 的方式來構建實時 Cube。
?
1)Kafka 數據格式要求
Kylin 的實時 Cube 需要配置基于 Kafka topic 的 Streaming Table (將 Kafka topic 映射成一張普通表)。這一步不同于基于 Hive 的數據表(Kylin 可以直接從 Hive metastore 獲取表的字段信息),需要管理員進行一定的手工配置,才能將 Kafka 中的 JSON 消息映射成表格中的行。這一步對 Kafka 中的數據格式和字段有一定的要求,起初因為不了解這些要求,我們配置的 Cube 在構建時經常失敗,只有少數 Cube 構建成功。也有的 Cube 很多次都構建成功,但偶爾會有失敗。針對這些問題我們進行了一系列的排查和改進。
?
a. 由于我們原始數據在 kafka 中的存放格式為數組格式(JSON 字符串),所以在創建 Streaming Table 的時候會遇到下面的問題:
Kylin 會將數組中識別的字段默認加上數組下標,例如圖中的 0_a,0_b 等,與我們的預期不符,所以需要對數組數據進行拆分。也就是說,Kylin 期望一條消息就是一個 JSON 對象(而非數組)。
?
b. 我們原始數據中還有嵌套的對象類型的字段,這種類型在 Kylin Streaming Table 識別的時候也可能會有問題,同樣需要規整。如 Kylin 會把嵌套格式如 "{A: {B: value}}" 識別為 A_B 的字段,如下圖,所以使用起來同樣也可以,這個根據業務的不同可以自由選擇,我們采用了將嵌套字段鋪平來規避后面可能出現的問題。
c. 這個是比較難發現的一個問題,就是在設計好 Cube 之后,有時會有 Cube 構建失敗的情況,經過排查之后發現,是由于公司業務數據來源的特殊性(來自于客戶端上報),所以可能會出現 Kafka 中字段不一致的情況。一旦出現極少數字段不一樣的數據混在 Kafka 中,便極有可能讓這一次的 Cube 構建失敗。
?
基于以上幾點,我們總結,Kylin 在接入 Kafka 實時數據構建之前,一定要做好數據清洗和規整,這也是我們前期耗費大量時間踩坑的代價。數據的清洗和規整我們采用的是流處理(Storm/Flink)對 Kafka 中的數據進行對應的處理,再寫入一個新的 Kafka topic 中供 Kylin 消費。
?
2)任務的定時調度
Cube 的構建任務需要調用 API,如何定時消費 kafka 的數據進行構建,以及消費 kafka 的機制究竟如何。由于對 Kylin 理解的不夠,一開始建出來的 Cube 消耗性能十分嚴重,需要對所建的 Cube 配合業務進行剪枝和優化。
?
構建實時 Cube 和構建基于 Hive 的離線 Cube 有很多不一樣的地方,我們在使用和摸索的過程中踩了很多坑,也有了一定的經驗。
?
由于是近實時 Cube 構建,需要每隔一小段時間就要構建一次,采用服務器中 Kylin 主節點上部署 Crontab-job 的模式來實現。
?
調度的時間間隔也經過了多次試驗,調度的時間短了,上一個任務還沒有執行完,下一個就開始了,容易產生堆積,在高峰時期容易造成滾雪球式的崩塌;調度的時間長了,一次處理的數據量就會特別大,導致任務執行的時間和消耗的資源也隨之增長(Kylin 取 Kafka的數據,是比較簡單粗暴的從上一次調度記錄的 offset 直接取到當前最新的 offset,所以間隔時間越長,數據量越多),這是一個需要平衡的事情。經過反復測試使用,以及官方相應的介紹下,我們發現任務執行時間在 10~20 分鐘為最優解,當然根據數據量的不同會有不同的調整。
?
3)Kylin 的剪枝與優化
由于業務比較復雜,每個 Cube 的維度可能特別的多,隨著維度數目的增加 Cuboid 的數量會爆炸式的增長。例如 WEB 端網絡性能分析的 Cube 維度可能達到 47 個,如果采用全量構建,每一個可能情況都需要的話,最多可能構建 2 的 47 次方,也就是1.4 * 10^14 種組合,這肯定是不能接受的。所以在 Cube 設計的時候一定要結合業務進行優化和剪枝。
?
首先是篩選,將原始數據中根據不同的業務,選擇不同的字段進行設計,以Ajax性能分析為例,選擇出需要使用的 25 種維度。(2^47 -> 2^25)
?
接下來是分組,將 25 種維度按照不同的場景進行分組,例如,地域相關的可以放在一起,瀏覽器相關的也能分為一組。我們將場景分為了 4 組,將指數增長拆分為多個維度組之和。好的分組可以有效的減少計算復雜度,但是沒有設計好的分組,很可能會由于設計問題沒有覆蓋好各種場景,導致查詢的時候需要二次聚合,導致查詢的性能很差,這里需要重點注意。(2^25 -> 2^12 + 2^13 + 2^14 + 2^13)
?
然后是層級維度(Hierarchy Dimensions)、聯合維度(Joint Dimensions)和必要維度(Mandatory Dimensions)的設置。這三個官網和網上都有大量的說明,這里不加贅述。最終實現 Kylin 的剪枝,來減少計算的成本。
?
最后是 Kylin 本身一系列的配置上的優化,這些針對各自業務和集群可以參照官方文檔進行調參優化。
?
3. Kylin 集群相關優化
起初我們為 Kylin 集群申請的機器類型是計算密集型,沒有足夠的本地存儲空間。Kylin 在運行的過程中磁盤經常滿了,常常需要手動清理機器。同時在前期運行的過程中時不時會出現「Kylin 服務掛了(或者管理端登不上)」,「HBase 掛了」等等情況,針對遇到的這幾個問題,我們有一些解決的措施。
?
1)磁盤不足。因為 Kylin 在構建 Cube 的時候,會產生大量的臨時文件,而且其中有部分臨時文件 Kylin 是不會主動刪除的,所以機器經常會出現磁盤空間不足的問題(也跟我們計算型機器磁盤空間小有關)。
?
解決辦法:采用定時自動清除,和手動調動 API 清除臨時文件,擴容 2 臺大容量機器調整 Reblance 比例(這才徹底解決這個問題)。
?
2)服務不穩定。剛開始的時候集群部分角色總是掛起(例如 HDFS、HBase 和 Kylin 等),排查發現是由于每臺機器存在多個角色,角色分配的內存之和大于機器的可用內存,當構建任務多時,可能導致角色由于內存問題掛掉。
?
解決辦法:對集群中各個角色重新分配,通過擴容可以解決一切資源問題。添加及時的監控,由于 Kylin 不在 CM 中管理,需要添加單獨的監控來判斷 Kylin 進程是否掛掉或者卡住,一旦發現需要重啟 Kylin。要注意有 job 的節點重啟時需要設置好 kafka 安裝路徑。
?
4. HBase 超時優化
Kylin 在后期維護中,經常會有任務由于 operationTimeout 導致任務失敗。如圖:
這個報錯讓 Cube 構建常常失敗,且一旦構建失敗超過一定的次數,該 Cube 就不會繼續構建了,影響到了業務的使用,針對此問題也進行了相應的排查,發現是構建的時候,可能會由于 HBase 連接超時或者是連接數不夠造成任務失敗。需要在 CM 中調整 HBase 相關參數。包括調整 hbase.rpc.timeout 和 hbase.client.operation.timeout 到 50000 等(之前是 30000,可以根據業務不不同自行調整,如果還有超時可以優化或者繼續調整)。
?
5. 已有 Cube 的修改
由于業務的迭代,新增了幾個維度和指標需要增加在已存在的Cube上,又例如原先 Cube 設計上有一些不足需要修改。在這方面例如 DataSource 沒有修改功能,新舊 Cube 如何切換,修改經常沒有響應等等問題讓我們十分為難。
?
已有 Cube 的修改是目前使用 Kylin 最為頭疼的地方。雖然 Kylin 支持 Hybrid model 來支持一定程度的修改,但是在使用的過程中因為各種各樣的原因,例如 Streaming Table 無法修改來新增字段等,還是未能修改成功。
?
目前采用的修改模式為,重新設計一整套從 DataSource 到 Model 再到 Cube,停止之前 Cube 的構建任務,開始新 Cube 的構建調度。使用修改我們 Java 代碼的方式動態的選擇查詢新 Cube 還是舊 Cube,等到一定的時間周期之后再廢棄舊 Cube。目前這種方式的弊端在于查詢時間段包含新舊時,需要在程序中拼接數據,十分麻煩且會造成統計數據不準。所以在設計之初就要多考慮一下后面的擴展,可以先預留幾個擴展字段。
?
效果對比
這里我們從多個角度對比一下幾個方案的優缺點:
?
| 條件 | Apache Kylin | ES?實時聚合 | Hive?離線任務再入 ?MySQL |
| 查詢速度 | 較快,一般在亞秒級別。從 HBase 中選擇適合的維度,Cude 設計的好的話不存在二次聚合,也不會有速度方面的問題 | 慢,可能有幾分鐘。實時聚合,在復雜的情況下有嚴重的性能問題,查詢的時間可能到幾分鐘。 | 快,一般在毫秒級。計算好的數據基于 ?MySQL 查詢,一般不會有性能問題。 |
| 時效性 | 近實時,一般在30分鐘以內。延遲主要取決于任務調度的時間,但是一般都會在10~30分鐘左右。 | 實時,一般延遲在秒級。ES的延遲是取決于上游數據的寫入延遲和數據刷新的時間,一般可以控制在秒級。 | 離線,一般是T+1延遲。離線數據由于同步和計算的關系,一般都是+1小時延遲或者是+1天延遲。 |
| 開發難度 | 較簡單。實時數據需要先進行一系列的清洗和規整,后面只需要配置即可,不過 Cube 設計有一定的難度。 | 簡單。只需要寫入數據即可,配合已有的EST框架可以任意組合滿足業務需要。 | 工作量極大。針對每一種維度組合,都需要手動開發任務來進行計算和存儲。 |
| 資源消耗 | 一般。單獨搭建的集群,不會對其他業務造成影響,但是集群資源需求還是比較大。 | 一般。查詢和寫入一旦量大復雜后對集群上其他的查詢會帶來影響。 | 一般。在大集群上跑 ?YARN 任務,對集群整體影響不大。 |
| 可擴展性 | 不太好擴展。針對已經建好的 ?DataSource、Model 和 Cube 的修改比較不友好,但是有解決的辦法。 | 可擴展性較強。所有的修改只需修改Index模板,下一周期生效即可。 | 基本無法擴展。每次有新的業務需求需要重新開發任務。 |
| 容錯性 | 較差。對數據的格式和類型要求較為嚴格,容易導致構建失敗。 | 較差。字段不一致會帶來沖突,導致字段無法聚合,且沖突一旦在索引中生成,該索引將無法解決,只有等待下一周期或刪除索引。 | 較好。對字段類型和數據字段有一定的容錯性。 |
| 數據查詢復雜度 | 十分簡單。Kylin?會根據條件自動識別就是在哪一個 Cuboid 中查詢數據,只需要使用 SQL 即可,跨 Cuboid 的查詢也可以自動二次聚合,SQL 也可以直接配合 EST 框架。 | 較為容易。配合 EST 框架查詢十分容易,但是由于索引有小時和天后綴,需要在程序中進行判斷,才能有效降低查詢量。 | 十分困難。由于每個維度存儲組合存儲的表都不一樣,導致存儲結構十分復雜,查詢的時候需要自己判斷在那張表里面,難度很大。 |
?
總結
基于以上的探索和使用,我們在使用 Kylin 之后,帶來最大的改變就是在查詢速度上的提升,給斗魚 APM 系統的用戶體驗上帶來了極大的改善:之前 ES 實時聚合需要將近 90 秒的查詢,改為 Kylin 只需 2~3 秒即可展示,原來需要加載幾分鐘的儀表盤,現在僅用幾秒就能全部加載完成,提高多達 30 倍。
?
在開發效率上,切換至 Kylin 在前期不熟悉的情況下的確走了一些彎路,踩了不少坑(跟數據質量、對 Kylin 原理的掌握等都有關)。但是后面在熟悉之后便可以有不遜色于 ES 的開發效率,用起來非常不錯。
?
目前版本的 Kylin 也有一些不足,例如數據的時效性,因為 Kylin 2.x 的流數據源只能達到準實時(Near Real-time),準實時延遲通常在十幾到幾十分鐘的,對 APM 系統中的實時告警模塊還不能滿足業務要求。所以目前實時告警這一塊走的還是ES,由于告警只需要對上個短暫周期(1~5 分鐘)內的數據做聚合,數據量較小,ES 對此沒有性能問題倒能承受。對于海量歷史數據,通過 Kylin 來查詢的效果更好。
?
新系統于 2018 年 11 月正式上線,目前已經穩定運行近一年。我們也注意到 Kylin 3.0 已經在實時統計上開始發力,能夠做到 ES 這樣的秒級延遲,我們會持續關注,希望 Kylin 可以發展的越來越好。
?
作者簡介:戴天力,斗魚高級大數據開發工程師,斗魚 Kylin 團隊主要負責人。
?
總結
以上是生活随笔為你收集整理的从 ES 到 Kylin,斗鱼客户端性能分析平台进化之旅的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hadoop企业优化
- 下一篇: 比 Redis 快 5 倍的中间件,Ke