Apache Doris 在思必馳的應用優化實踐:海量語音通話數據下,實時、離線一體的數倉架構設計實踐

語言: CN / TW / HK

業務背景

思必馳是一家對話式人工智能平台公司,擁有全鏈路的智能語音語言技術,致力於成為全鏈路智能語音及語言交互的平台型企業,自主研發了新一代人機交互平台 DUI 和人工智能芯片TH1520,為車聯網、IoT 及政務、金融等眾多行業場景合作伙伴提供自然語言交互解決方案。

思必馳於 2019 年首次引入Apache Doris ,基於 Apache Doris 構建了實時與離線一體的數倉架構。相對於過去架構,Apache Doris 憑藉其靈活的查詢模型、極低的運維成本、短平快的開發鏈路以及優秀的查詢性能等諸多方面優勢,如今已經在實時業務運營、自助/對話式分析等多個業務場景得到運用,滿足了設備畫像/用户標籤、業務場景實時運營、數據分析看板、自助 BI、財務對賬等多種數據分析需求。在這一過程中我們也積累了諸多使用上的經驗,在此分享給大家。

架構演進

早期業務中,離線數據分析是我們的主要需求,近幾年,隨着業務的不斷髮展,業務場景對實時數據分析的要求也越來越高,早期數倉架構逐漸力不從心,暴露出很多問題。為了滿足業務場景對查詢性能、響應時間及併發能力更高的要求,2019 年正式引入 Apache Doris 構建實時離線一體的數倉架構。

以下將為大家介紹思必馳數倉架構的演進之路,早期數倉存在的優缺點,同時分享我們選擇 Apache Doris 構建新架構的原因以及面臨的新問題與挑戰。

早期數倉架構及痛點

如上圖所示,早期架構基於 Hive +Kylin 來構建離線數倉,實時數倉架基於 Spark+MySQL 來構建實時分析數倉。

我們業務場景的數據源主要分為三類,業務數據庫如 MySQL,應用系統如 K8s 容器服務日誌,還有車機設備終端的日誌。數據源通過 MQTT/HTTP 協議、業務數據庫 Binlog 、Filebeat 日誌採集等多種方式先寫入 Kafka。

在早期架構中,數據經 Kafka 後將分為實時和離線兩條鏈路,首先是實時部分,實時部分鏈路較短,經過 Kafka 緩衝完的數據通過 Spark 計算後放入 MySQL 中進行分析,對於早期的實時分析需求,MySQL 基本可以滿足分析需求。而離線部分則由 Spark 進行數據清洗及計算後在 Hive 中構建離線數倉,並使用 Apache Kylin 構建 Cube,在構建 Cube 之前需要提前做好數據模型的的設計,包括關聯表、維度表、指標字段、指標需要的聚合函數等,通過調度系統進行定時觸發構建,最終使用 HBase 存儲構建好的 Cube。

早期架構的優勢:

  1. 早期架構與 Hive 結合較好,無縫對接 Hadoop 技術體系。

  2. 離線數倉中基於 Kylin 的預計算、表關聯、聚合計算、精確去重等場景,查詢性能較高,在併發場景下查詢穩定性也較高。

早期架構解決了當時業務中較為緊迫的查詢性能問題,但隨着業務的發展,對數據分析要求不斷升高,早期架構缺點也開始逐漸凸顯出來。

早期架構的痛點:

  1. 依賴組件多。Kylin 在 2.x、3.x 版本中強依賴 Hadoop 和 HBase ,應用組件較多導致開發鏈路較長,架構穩定性隱患多,維護成本比很高。

  2. Kylin 的構建過程複雜,構建任務容易失敗。Kylin 構建需要進行打寬表、去重列、生成字典,構建 Cube 等如果每天有 1000-2000 個甚至更多的任務,其中至少會有 10 個甚至更多任務構建失敗,導致需要大量時間去寫自動運維腳本。

  3. 維度/字典膨脹嚴重。維度膨脹指的是在某些業務場景中需要多個分析條件和字段,如果在數據分析模型中選擇了很多字段而沒有進行剪枝,則會導致 Cube 維度膨脹嚴重,構建時間變長。而字典膨脹指的是在某些場景中需要長時間做全局精確去重,會使得字典構建越來越大,構建時間也會越來越長,從而導致數據分析性能持續下降。

  4. 數據分析模型固定,靈活性較低。在實際應用過程中,如果對計算字段或者業務場景進行變更,則要回溯部分甚至全部數據。

  5. 不支持數據明細查詢。早期數倉架構是無法提供明細數據查詢的,Kylin 官方給的解決方法是下推給 Presto 做明細查詢,這又引入了新的架構,增加了開發和運維成本。

架構選型

為解決以上問題,我們開始探索新的數倉架構優化方案,先後對市面上應用最為廣泛的 Apache Doris、Clickhouse 等 OLAP 引擎進行選型調研。相較於 ClickHouse 的繁重運維、各種各樣的表類型、不支持關聯查詢等,結合我們的 OLAP 分析場景中的需求,綜合考慮,Apache Doris 表現較為優秀,最終決定引入 Apache Doris 。

新數倉架構

如上圖所示,我們基於 Apache Doris 構建了實時+離線一體的新數倉架構,與早期架構不同的是,實時和離線的數據分別進行處理後均寫入 Apache Doris 中進行分析。

因歷史原因數據遷移難度較大,離線部分基本和早期數倉架構保持一致,在 Hive 上構建離線數倉,當然完全可以在 Apache Doris 上直接構建離線數倉。

相對早期架構不同的是,離線數據通過 Spark 進行清洗計算後在 Hive 中構建數倉,然後通過 Broker Load 將存儲在 Hive 中的數據寫入到 Apache Doris 中。這裏要説明的, Broker Load 數據導入速度很快,天級別 100-200G 數據導入到 Apache Doris 中僅需要 10-20 分鐘。

實時數據流部分,新架構使用了 Doris-Spark-Connector 來消費 Kafka 中的數據並經過簡單計算後寫入 Apache Doris 。從架構圖所示,實時和離線數據統一在 Apache Doris 進行分析處理,滿足了數據應用的業務需求,實現了實時+離線一體的數倉架構。

新架構的收益

  1. 極簡運維,維護成本低,不依賴 Hadoop 生態組件。Apache Doris 的部署簡單,只有 FE 和 BE 兩個進程, FE 和 BE 進程都是可以橫向擴展的,單集羣支持到數百台機器,數十 PB 的存儲容量,並且這兩類進程通過一致性協議來保證服務的高可用和數據的高可靠。這種高度集成的架構設計極大的降低了一款分佈式系統的運維成本。在使用 Doris 三年時間中花費的運維時間非常少,相比於基於 Kylin 搭建的早期架構,新架構花費極少的時間去做運維。

  2. 鏈路短,開發排查問題難度大大降低。基於 Doris 構建實時和離線統一數倉,支持實時數據服務、交互數據分析和離線數據處理場景,這使得開發鏈路變的很短,問題排查難度大大降低。

  3. 支持 Runtime 形式的 Join 查詢。Runtime 類似 MySQL 的表關聯,這對數據分析模型頻繁變更的場景非常友好,解決了早期結構數據模型靈活性較低的問題。

  4. 同時支持 Join、聚合、明細查詢。解決了早期架構中部分場景無法查詢數據明細的問題。

  5. 支持多種加速查詢方式。支持上卷索引,物化視圖,通過上卷索引實現二級索引來加速查詢,極大的提升了查詢響應時間。

  6. 支持多種聯邦查詢方式。支持對 Hive、Iceberg、Hudi 等數據湖和 MySQL、Elasticsearch 等數據庫的聯邦查詢分析。

問題和挑戰

在建設新數倉架構過程中,我們遇到了一些問題:

  • 高併發場景對 Apache Doris 查詢性能存在一定影響。我們分別在 Doris 0.12 和 Doris 1.1 版本上進行測試,同一時間同樣的 SQL,10 併發和 50 併發進行訪問,性能差別較大。

  • 在實時寫入場景中,當實時寫入的數據量比較大時,會使得 IO 比較密集,導致查詢性能下降。

  • 大數據量下字符串精確去重較慢。目前使用的是 count distinct 函數、Shuffle 和聚合算子去重,此方式算力比較慢。當前業內常見的解決方法一般是針對去重列構建字典,基於字典構建 Bitmap 索引後使用 Bitmap 函數去重。目前 Apache Doris 只支持數字類型的 Bitmap 索引,具有一定的侷限性。

業務場景的應用

Apache Doris 在思必馳最先應用在實時運營業務場景以及自助/對話式分析場景,本章節將介紹兩個場景的需求及應用情況。

實時運營業務場景

首先是實時運營業務場景,如上圖所示,實時運營業務場景的技術架構和前文所述的新版數倉架構基本一致:

  • 數據源:數據源新版架構圖中一致,包括 MySQL 中的業務數據,應用系統埋點數據以及設備和終端日誌。

  • 數據導入:離線數據導入使用 Broker Load,實時數據導入使用 Doris-Spark-Connector 。

  • 數據存儲與開發:幾乎所有的實時數倉全部在 Apache Doris 構建,有一部分離線數據放在 Airflow 上執行 DAG 跑批任務。

  • 數據應用:最上層是業務側提出的業務分析需求,包括大屏展示,數據運營的實時看板、用户畫像、BI 看板等。

在實時運營業務場景中,數據分析的需求主要有兩方面:

  • 由於實時導入數據量比較大,因此對實時數據的查詢效率要求較高

  • 在此場景中,有 20+ 人的團隊在運營,需要同時開數據運營的看板,因此對實時寫入的性能和查詢併發會有比較高的要求。

自助/對話式分析場景

除以上之外,Apache Doris 在思必馳第二個應用是自助/對話式分析場景。

如上圖所示,在一般的 BI 場景中,用户方比如商務、財務、銷售、運營、項目經理等會提出需求給數據分析人員,數據分析人員在 BI 平台上做數據看板,最終把看板提供給用户,用户從 BI 看板上獲取所需信息,但是有時候用户想要查看明細數據、定製化的看板需求,或者在某些場景需做任意維度的上卷或者下鑽的分析,一般場景下 BI 看板是不支持的的,基於以上所述用户需求,我們打造了自助對話式 BI 場景來解決用户定製化的需求。

與一般 BI 場景不同的是,我們將自助/對話式 BI 場景從數據分析人員方下沉到用户方,用户方只需要通過打字,描述數據分析的需求。基於我們公司自然語言處理的能力,自助/對話式 BI 場景會將自然語言轉換成 SQL,類似 NL2SQL 技術,需要説明的是這裏使用的是定製的自然語言解析,相對開源的 NL2SQL 命中率高、解析結果更精確。當自然語言轉換成 SQL 後,將 SQL 給到 Apache Doris 查詢得到分析結果。由此,用户通過打字就可以隨時查看任意場景下的明細數據,或者任意字段的上卷、下鑽。

相比 Apache Kylin、Apache Druid 等預計算的 OLAP 引擎,Apache Doris 符合以下幾個特點:

  • 查詢靈活,模型不固定,支持自由定製場景。

  • 支持表關聯、聚合計算、明細查詢。

  • 響應時間要快速。

因此我們很順利的運用 Apache Doris 實現了自助/對話式分析場景。同時,自助/對話式分析在我們公司多個數據分析場景應用反饋非常好。

實踐經驗

基於上面的兩個場景,我們使用過程當中積累了一些經驗和心得,分享給大家。

數倉表設計

  1. 千萬級(量級供參考,跟集羣規模有關係)以下的數據表使用 Duplicate 表類型,Duplicate 表類型同時支持聚合、明細查詢,不需要額外寫明細表。

  2. 當數據量比較大時,使用 Aggregate 聚合表類型,在聚合表類型上做上卷索引,使用物化視圖優化查詢、優化聚合字段。由於 Aggregate 表類型是預計算表,會丟失明細數據,如有明細查詢需求,需要額外寫一張明細表。

  3. 當數據量又大、關聯表又多時,可用 ETL 先寫成寬表,然後導入到 Doris,結合 Aggregate 在聚合表類型上面做優化,也可以使用官方推薦 Doris 的 Join 優化

寫入

  1. 通過 Spark Connector 或 Flink Connector 替代 Routine Load: 最早我們使用的是 Routine Load 實時寫入 BE 節點, Routine Load 的工作原理是通過 SQL 在 FE 節點起一個類似於 Task Manager 的管理,把任務分發給 BE 節點,在 BE 節點起 Routine Load 任務。在我們實時場景併發很高的情況下,BE 節點 CPU 峯值一般會達到 70% 左右,在這個前提下,Routine Load 也跑到 BE 節點,將嚴重影響 BE 節點的查詢性能,並且查詢 CPU 也將影響 Routine Load 導入, Routine Load 就會因為各種資源競爭死掉。面對此問題,目前解決方法是將 Routine Load 從 BE 節點拿出來放到資源調度上,用 Doris-Spark/Flink-Connector 替換 Routine Load。當時 Doris-spark-Connector 還沒有實時寫入的功能,我們根據業務需求進行了優化,並將方案貢獻給社區。

  2. 通過攢批來控制實時寫入頻率:當實時寫入頻率較高時,小文件堆積過多、查詢 IO 升高,小文件排序歸併的過程將導致查詢時間加長,進而出現查詢抖動的情況。當前的解決辦法是控制導入頻次,調整 Compaction 的合併線程、間隔時間等參數,避免 Tablet 下小文件的堆積。

查詢

  1. 增加 SQL 黑名單,控制異常大查詢。個別用户在查詢時沒有加 where 條件,或者查詢時選擇的時間範圍較長,這種情況下 BE 節點的 SQL 會把磁盤的負載和 CPU 拉高,導致其他節點的 SQL 查詢變慢,甚至出現 BE 節點宕機的情況。目前的解決方案是使用 SQL 黑名單禁止全表及大量分區實時表的查詢。

  2. 使用 SQL Cache 和 SQL Proxy 實現高併發訪問。同時使用 SQL Cache 和 SQL Proxy 的原因在於,SQL Cache 的顆粒度到表的分區,如果數據發生變更, SQL Cache 將失效,因此 SQL Cache 緩存適合數據更新頻次較低的場景(離線場景、歷史分區等)。對於數據需要持續寫到最新分區的場景, SQL Cache 則是不適用的。當 SQL Cache 失效時 Query 將全部發送到 Doris 造成重複的 Runtime 計算,而 SQL Proxy 可以設置一秒左右的緩存,可以避免相同條件的重複計算,有效提高集羣的併發。

存儲

使用 SSD 和 HDD 做熱温數據存儲週期的分離,近一年以內的數據存在 SSD,超過一年的數據存在 HDD。Apache Doris 支持對分區設置冷卻時間,但只支持創建表分區時設置冷卻的時間,目前的解決方案是設置自動同步邏輯,把歷史的一些數據從 SSD 遷移到 HDD,確保 1 年內的數據都放在 SSD 上。

升級

升級前一定要備份元數據,也可以使用新開集羣的方式,通過 Broker 將數據文件備份到 S3 或 HDFS 等遠端存儲系統中,再通過備份恢復的方式將舊集羣數據導入到新集羣中。

升級前後性能對比

思必馳最早是從 0.12 版本開始使用 Apache Doris 的,在今年我們也完成了從 0.15 版本到最新 1.1 版本的升級操作,並進行了基於真實業務場景和數據的性能測試。

從以上測試報告中可以看到,總共 13 個測試 SQL 中,前 3 個 SQL 升級前後性能差異不明顯,因為這 3 個場景主要是簡單的聚合函數,對 Apache Doris 性能要求不高,0.15 版本即可滿足需求。而在 Q4 之後的場景中 ,SQL 較為複雜,Group By 有多個字段、多個字段聚合函數以及複雜函數,因此升級新版本後帶來的性能提升非常明顯,平均查詢性能較 0.15 版本提升 2-3 倍。由此,非常推薦大家去升級到 Apache Doris 最新版本。

總結和收益

  1. Apache Doris 支持構建離線+實時統一數倉,一個 ETL 腳本即可支持實時和離線數倉,大大縮短開發週期,降低存儲成本,避免了離線和實時指標不一致等問題。

  2. Apache Doris 1.1.x 版本開始全面支持向量化計算,較之前版本查詢性能提升 2-3 倍。經測試,Apache Doris 1.1.x 版本的查詢性能已接近 ClickHouse。

  3. 功能強大,不依賴其他組件。相比 Apache Kylin、Apache Druid、ClickHouse 等,Apache Doris 不需要引入第 2 個組件填補技術空檔。Apache Doris 支持聚合計算、明細查詢、關聯查詢,當前思必馳超 90% 的分析需求已移步 Apache Doris 實現。 得益於此優勢,技術人員需要運維的組件減少,極大降低運維成本。

  4. 易用性極高,支持 MySQL 協議和標準 SQL,大幅降低用户學習成本。

未來計劃

  1. Tablet 小文件過多的問題。Tablet 是 Apache Doris 中讀寫數據最小的邏輯單元,當 Tablet 小文件比較多時會產生 2 個問題,一是 Tablet 小文件增多會導致元數據內存壓力變大。二是對查詢性能的影響,即使是幾百兆的查詢,但在小文件有幾十萬、上百萬的情況下,一個小小的查詢也會導致 IO 非常高。未來,我們將做一個 Tablet 文件數量/大小比值的監控,當比值在不合理範圍內時及時進行表設計的修改,使得文件數量和大小的比值在合理的範圍內。

  2. 支持基於 Bitmap 的字符串精確去重。業務中精確去重的場景較多,特別是基於字符串的 UV 場景,目前 Apache Doris 使用的是 Distinct 函數來實現的。未來我們會嘗試的在 Apache Doris 中創建字典,基於字典去構建字符串的 Bitmap 索引。

  3. Doris-Spark-Connector 流式寫入支持分塊傳輸。Doris-Spark-Connector 底層是複用的 Stream Load,工作機制是攢批,容易出現兩個問題,一是攢批可能會會出現內存壓力導致 OOM,二是當 Doris-Spark-Connector 攢批時,Spark Checkpoint 沒有提交,但 Buffer 已滿並提交給 Doris,此時 Apacche Doris 中已經有數據,但由於沒有提交 Checkpoint,假如此時任務恰巧失敗,啟動後又會重新消費寫入一遍。未來我們將優化此問題,實現 Doris-Spark-Connector 流式寫入支持分塊傳輸。

作者介紹

趙偉,思必馳大數據高級研發,10 年大數據開發和設計經驗,負責大數據平台基礎技術和 OLAP 分析技術開發。社區貢獻:Doris-spark-connector 的實時讀寫和優化。