從 ES 到 Kylin,鬥魚客戶端性能分析平臺進化之旅

遊戲直播行業龍頭鬥魚在 2019 年 Q2 的平均 MAU 再創新高,達到 1.628 億。天天,超大量的用戶使用鬥魚各客戶端參與線上互動,鬥魚須要對客戶端採集到的性能數據進行統計和分析,開發出具備多維度分析圖表和數據監控的 APM (Application Performance Monitoring,應用性能監控) 平臺。最初,鬥魚採用了市面上很是流行的 Elasticsearch (簡稱 ES)實時聚合實現。運行一段時間後,基於 ES 的方案面臨用戶查詢時間長、數據精度丟失等問題,鬥魚採用 Apache Kylin 替換 Elasticsearch, 對 APM 平臺中存在的問題進行優化。不試不知道,一試嚇一跳。數組

 

1、背景

鬥魚是一家面向大衆用戶的在線直播平臺,天天都有超大量的終端用戶在使用鬥魚各客戶端參與線上互動。伴隨業務的迅猛發展,鬥魚須要對客戶端採集到的性能數據進行統計和分析,開發出具備多維度分析圖表和數據監控的 APM (Application Performance Monitoring,應用性能監控) 平臺。瀏覽器

針對不一樣的客戶端採集的不一樣數據,咱們須要將各類維度之間相互組合並聚合,最終產出的數據變成指標在圖表中展現。例如:對在時間、地域、網絡環境、客戶端以及 CDN 廠商等維度聚合下的各項指標狀況進行多維度分析,包括客戶端網絡性能(包含完整請求耗時,請求耗時,響應耗時,DNS 耗時,TCP 耗時,TLS 耗時等等指標)各種錯誤時間段內的佔比以及詳細數量、狀態碼分佈等等。圖一和圖二分別是兩個示例:服務器

△ 圖一網絡

△ 圖二框架

咱們最初的 APM 平臺採用了市面上很是流行的 Elasticsearch (簡稱 ES)實時聚合實現。配合自研多數據源統一接口(EST 多數據源統一接口平臺)框架,可以實現維度指標的自由組合查詢。數據採用 storm 實時消費 kafka 寫入 ES,作到了數據的實時展現。告警採用定時查詢 ES 的方式。oop

不過運行一段時間後,咱們發現基於 ES 的方案存在問題:採用 ES 實時聚合的方式,大多數時候對單個字段的聚合查詢是很是快的,一旦遇到較爲複雜的多維度組合查詢而且聚合的數據量比較大(如數十億),就可能會產生大量的分組,對 ES 的性能壓力很大,查詢時間很長(幾十秒到數分鐘)致使用戶難以等待,還可能會遇到數據精度丟失的問題。所以爲了支撐業務發展,考慮再三咱們決定尋找替代方案,注意到 Apache Kylin 在大數據 OLAP 分析方面很是有優點,因而決定採用 Kylin 替換 Elasticsearch, 對鬥魚 APM 平臺中存在的問題進行優化。不試不知道,一試嚇一跳,效果還真的不錯。性能

 

2、使用 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。目前這種方式的弊端在於查詢時間段包含新舊時,須要在程序中拼接數據,十分麻煩且會形成統計數據不許。因此在設計之初就要多考慮一下後面的擴展,能夠先預留幾個擴展字段。

 

3、效果對比

這裏咱們從多個角度對比一下幾個方案的優缺點:

條件 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 框架查詢十分容易,可是因爲索引有小時和天后綴,須要在程序中進行判斷,纔能有效下降查詢量。 十分困難。因爲每一個維度存儲組合存儲的表都不同,致使存儲結構十分複雜,查詢的時候須要本身判斷在那張表裏面,難度很大。

 

4、總結

基於以上的探索和使用,咱們在使用 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 團隊主要負責人。

 

瞭解更多大數據資訊,點擊進入Kyligence官網

相關文章
相關標籤/搜索