作者:滴滴 OLAP 開發工程師 劉雨飛
小編導讀:
滴滴于 2022 年引入了 StarRocks。經過一年多的努力,StarRocks 逐漸替代了原有技術棧,成為滴滴內部主要的 OLAP 引擎。截至 2023 年 12 月,滴滴已經成功建立了超過 40 個 StarRocks 集群,每日查詢量在千萬量級,擁有超過 3000 張數據表。這一強大的基礎設施已廣泛支持了滴滴公司幾乎所有的業務線,包括網約車、單車、能源、貨運等多個領域。
?? 關于滴滴統一 OLAP 引擎的詳細內容已在“StarRocks 統一 OLAP 引擎在滴滴的探索實踐”進行了詳細講解,本文將著重探討 StarRocks 物化視圖在滴滴的實際應用。
高并發精準去重
實時數據洞察的攔路虎
實時數據洞察在業務管理中的重要性無可替代。在滴滴內部,網約車實時看板是公司最關鍵的業務監控工具之一,包含著超過 20 個重要的業務指標,如實時呼叫量、冒泡數量和 GMV 等。這些實時看板數據對于業務、數據和運營團隊至關重要,提供了即時見解,幫助我們更好地了解業務的狀態和趨勢。
通過實時監測這些數據,我們能夠迅速應對市場波動,做出及時決策,例如:
-
實時呼叫量變化可幫助我們滿足高峰需求,縮短乘客等待時間,提高司機收益; -
冒泡數量波動可提示哪些地區需增加司機資源,滿足潛在乘客需求; -
然而,關鍵業務指標的計算通常需要大量的精確去重操作,尤其在高峰訪問期間,這可能會對資源造成巨大壓力,也是眾多 OLAP 系統一直以來的挑戰之一。為了平衡計算成本,許多系統犧牲了準確性,提供模糊去重的方法,這也是滴滴過去基于 Druid 的指標計算方案所采用的策略。
但是,模糊去重方法存在計算誤差,這意味著我們可能無法完全準確地反映業務的實際情況,從而難以實現更精細化的運營決策。此外,即便為了降低資源消耗而犧牲了準確性,模糊去重節省的資源在高并發場景下仍舊是杯水車薪。在大規模促銷活動期間,高并發查詢可能導致 Druid 集群的穩定性問題,引發性能下降、響應時間延長甚至系統崩潰,這對一個依賴實時數據的業務來說是不可接受的。
因此,我們迫切需要一種新的解決方案,在可控的資源開銷下,實現高并發的精確去重,以更好地支持業務運營和決策。StarRocks 的物化視圖幫助我們實現了這一目標。
StarRocks 物化視圖 讓精準去重的落地和加速不再是紙上談兵
實時看板場景具有以下顯著特點:
高基數的精確去重。看板需要面對日增量上億規模的明細數據,并針對明細數據進行大量 count(distinct()) 計算。并且,在實際應用中,精確去重的用戶、訂單 id 通常是字符串,這對計算資源造成了巨大挑戰。
支持靈活的維度篩選。看板查詢提供了多種篩選條件,包括時間、城市、業務線等超過十個維度字段的組合。每天的真實查詢可能會涉及上千種維度組合。 查詢并發高。尤其在大促期間,數據開發以及業務運營都會密切關注業務瞬時變化趨勢,高峰時期可能有上千規模的用戶盯盤。指標數據按照分鐘級別刷新,每次刷新會觸發大約幾十次查詢計算,則高峰時期可能有數百 QPS,對集群負載要求非常高。如果每次查詢都直接使用原始明細數據進行計算,將消耗大量計算資源,這在成本上是難以接受的。
這樣的業務場景對查詢引擎提出了非常高的要求。自 2022 年起,滴滴已經在網約車、單車、能源、貨運等多個領域應用了 StarRocks,我們也在密切關注社區的新功能。異步物化視圖是針對透明查詢加速、高并發場景研發的利器,自其發布之后,我們就開始了這個場景的測試驗證。嘗試物化視圖主要是由于其以下幾個特點:
-
物化查詢結果:物化視圖可以通過 SQL 查詢來將計算結果緩存下來。緩存下來的結果可以被看作是一張物理表,相比 index(同步物化視圖),在高并發的場景下表現更加優秀。
-
托管刷新流程:相較于手動維護導入任務,物化視圖的刷新不僅可以定時觸發,還能夠根據數據變更做到分區級別的增量刷新,降低刷新成本。
-
透明查詢改寫:物化視圖支持智能的透明改寫,能夠將物化的結果更大范圍的應用到更多查詢加速上。用戶無需感知物化視圖的存在,即可加速查詢體驗。
StarRocks 實時精確去重最佳實踐
在結合業務特點和 StarRocks 的物化視圖能力,我們設計了整個看板場景的加速優化鏈路。以下是設計的主要思路:

為了最大限度地降低去重操作對 CPU 的消耗、加速去重,我們的整體思路是:
-
數據預處理:利用全局字典進行數據類型轉換,用最小的代價做精確去重
最上游的數據來自數據倉庫,經過清洗加工后,通過 Flink 實時同步到 StarRocks。在 StarRocks 內部,首先進行一次全局字典轉換。特別是對需要進行去重的指標列,將其從 String 類型映射為 BIGINT 類型,以便后續使用 BITMAP 類型進行上卷計算。
-
ODS 層:利用明細模型存儲原始數據
接下來,在 StarRocks 內部進行數據建模,生成原始明細表并形成 ODS 層--StarRocks 明細模型層。
-
DWD 層:利用同步物化視圖構建增量聚合層
在 DWD 層,我們創建了同步物化視圖對不同維度組合進行上卷去重。同步物化視圖類似于索引(Index),它具有較高的時效性,并且數據滿足強一致性。在精確去重場景,同步物化視圖可以通過存儲 BITMAP 類型的中間計算結果,使后續單次明細查詢性能明顯提升,同時還為下一步異步物化視圖的更新提供便利。
-
ADS 層:利用異步物化視圖構建透明加速層
在 ADS 層,我們創建了異步物化視圖,這些視圖使用定時刷新機制。雖然時效性相對較差,存在一定的數據更新延遲,但由于存儲的是最終計算結果,查詢速度非常快。這可以被視為持久化的查詢緩存。
-
加速策略:利用智能改寫能力透明提速
通過分析歷史查詢模式來構建增量聚合層的同步物化視圖,在此之上進一步將最高頻的查詢定義為透明加速層的異步物化視圖。同步和異步物化視圖都支持透明的查詢改寫,依照這樣的構建邏輯,用戶基于原始明細表查詢時,會遵循異步物化視圖->同步物化視圖->原始明細表的優先級來進行查詢加速,從而保證了查詢整體的實效性。
接下來展開講解每一部分是如何構建的。
數據預處理:利用全局字典進行數據類型轉換,用最小的代價做精確去重
StarRocks 支持兩種類型的去重方式:Hyper-loglog 和 BITMAP。對于需要精確去重的指標,我們使用 BITMAP 類型。
StarRocks 內部采用了 Roaring BITMAP 的實現方式,字段類型要求在 INT(64) 位以內,并在數據連續性較好的情況下性能表現更佳。如果數據具有連續遞增的特性,相較于完全隨機的 ID 性能優勢可達數倍以上。因此,滴滴在 StarRocks 中實現了高基數全局字典的功能。

第一步:創建全局字典表(主鍵模型表)
這里利用到 StarRocks 主鍵模型表的部分列更新以及自增列功能。文檔請見:
https://docs.starrocks.io/zh-cn/latest/using_starrocks/query_acceleration_with_auto_increment
創建全局字典表:數據存儲在 StarRocks 內部具有自增 ID 列的主鍵表中。表的主鍵使用需要去重的 STRING 字段,而 ID 列則是自增的 BIGINT 列。這樣每一個需要被去重的 STRING 字段都會有一個對應的 BIGINT 值與之對應。
連續遞增的數據生成:在寫入 STRING 列數據時,自增 ID 列會自動生成連續遞增的 BIGINT 值。并且,通過 StarRocks 的 partial_update 部分列更新功能,可以保證 BIGINT 列只在第一次寫入時生成,后續即便寫入相同的 STRING 值,對應的 BIGINT 也不會被更新。確保數據寫入的冪等性,從而保證數據可以無限次地重復寫入。
第二步:字典映射函數
我們實現了字典映射的函數 dict_mapping,其入參包括字典表表名和主鍵值。它可以在計算時實時查詢字典表,并返回生成的 ID 值。為了提高性能,我們使用了 StarRocks 的主鍵索引進行加速,相比于基于直接掃描,性能提升非常顯著。這個函數目前已經貢獻回社區,歡迎大家使用。
第三步:Flink 數據寫入
對 Flink 的 flink-starrocks-connector 進行改造,數據寫入分為兩步:
首先,將數據寫入字典表,包括抽取需要寫入字典表的列,以確保數據寫入并落盤,同時提交事務以使其可見。
隨后,將數據寫入明細模型表。在寫入時,StarRocks 支持設置參數和使用函數進行預處理。使用第二步的字典映射函數 dict_mapping 對需要去重的字段進行重新映射,將原本的 string 類型映射為字典表中 ID 列的值。
通過這一流程,實現了在 StarRocks 中的全局字典功能,用于高效處理需要精確去重的指標計算。這個優化方案顯著提高了性能并降低了計算成本,使得處理高基數去重變得更加高效和可擴展。
DWD 層:利用同步物化視圖構建增量聚合層
在增量聚合層我們創建了同步物化視圖。同步物化視圖同樣提供了基于 BITMAP 和 HLL 算法的去重方式。我們可以方便地利用 BITMAP 聚合函數來創建同步物化視圖。同步物化視圖創建完成后,后續查詢語句中的子查詢 count(distinct order_id) 會被自動改寫為 bitmap_union_count(to_bitmap(order_id)) 以便查詢命中物化視圖。例如:
CREATE?MATERIALIZED?VIEW?mv_dwd?AS
SELECT?dt,??time_slice(`table_time`,?INTERVAL?5?minute,?floor)?AS?`ts`,?city,?source_type,?bitmap_union(to_bitmap(order_id))
FROM?base_table
GROUP?BY?dt,?ts,?city,?source_type;
同步物化視圖不僅可以作為異步物化視圖的中間計算結果,加速異步物化視圖的刷新,也可以承載一部分異步物化視圖未命中的查詢對其加速。
ADS層:利用異步物化視圖構建透明加速層
在增量聚合層我們創建了異步物化視圖。這里以簡化后的訂單表為例,介紹如何通過創建異步視圖來實現查詢加速。訂單表包括分區日期、數據時間、城市、渠道、業務線等維度字段,以及需要去重的字段業務訂單 ID。
在數據表中,我們使用time_slice函數將時間粒度取整為 5 分鐘,并按照 5 分鐘區間粒度進行數據聚合。聚合的維度包括城市、渠道等可累加的維度。這個聚合視圖的好處在于,當用戶需要查詢 5 分鐘粒度的數據,并且查詢條件與視圖中的聚合維度完全匹配時,可以直接使用這個視圖進行查詢加速,而無需查詢原始底表的明細數據。示例如下:
CREATE?MATERIALIZED?VIEW?`mv_ads`
?PARTITION?BY?(dt)
?DISTRIBUTED?BY?HASH(`ts`)?BUCKETS?1
?REFRESH?ASYNC?START(“2023-06-28?21:00:00”)?EVERY(INTERVAL?30?SECOND)?PROPERTIES?(?
"partition_refresh_number"?=?"3"?)?
AS?SELECT?
`dt`,
?time_slice(`table_time`,?INTERVAL?5?minute,?floor)?AS?`ts`,?`city`,
?`source_type`,
?count(DISTINCT?`order_id`)?AS?`order_num`?
FROM?`base_table`?GROUP?BY?
`dt`,`ts`,`city`,?`source_type`;?
同樣的操作可以重復進行,設置不同的區間聚合粒度(如 1 分鐘、10 分鐘、30 分鐘等),并按照不同的維度列組合,創建多張異步視圖。這樣可以滿足不同用戶、不同維度組合的查詢需求,實現了對應實時看板的查詢加速效果。 在上述過程中,需要創建的異步物化視圖數量可以通過以下方式進行優化,以降低視圖建設的成本:
-
區分出可累加維度和不可累加維度
根據訂單表中維度列的查詢特點,可以將維度列分為可累加維度和不可累加維度。可累加維度指的是包含隸屬關系的維度。例如,“城市”屬于可累加維度,因為它隸屬于“全國”這個概念;而訂單狀態是不可累加維度,因為每個訂單都在不同狀態之間流轉。
-
對于可累加維度,僅創建必要的異步視圖
對于可累加維度,只需創建一個基于該維度的異步物化視圖,不需要為每個不同的可累加維度組合創建單獨的視圖,因為結果是可以復用的。例如只需要創建針對“城市”聚合的物化視圖,當需要計算“全國”范圍的結果時,可以在城市的結果上進一步計算而來。如果所有維度都是可累加維度,則理論上只需要一張物化視圖就可以解決。
-
對于不可累加維度,根據不同組合創建異步視圖
對于不可累加維度,需要根據不同的不可累加維度創建異步物化視圖。這些視圖將單獨處理每個不可累加維度的數據,以滿足特定查詢條件。
通過以上優化策略,如果數據表中有 M 個可累加維度列和 N 個不可累加維度列,那么只需要創建 2^(N-M) 個異步物化視圖,而不是 2^N 個。這樣可以大幅減少視圖數量的同時滿足絕大多數查詢條件的加速需求,降低了建設和維護的成本,提高了系統的可維護性和效率。
加速策略:利用智能改寫能力透明提速
StarRocks 提供了物化視圖的透明加速功能,使用戶可以無需手動選擇使用哪張表,而是通過自動改寫查詢來實現加速,同時保持查詢的語義一致性和性能提升。
假設有以下查詢示例:
SELECT?
??ts,?
??SUM(order_num)?
FROM(
????SELECT?
??????time_slice(`table_time`,?interval?5?minute)?AS?ts,?
??????count(DISTINCT?`order_id`)?AS?`order_num`?
????FROM?
??????`base_table`?
????WHERE?
??????(...)?
????GROUP?BY?
??????`dt`,?
??????`ts`,?
??????`city`,?
??????`source_type`
??)?sub?
WHERE?
??dt?=?'2023-07-01'?
GROUP?BY?
??ts;?
在這個查詢中,內層子查詢對數據進行了 5 分鐘粒度的聚合,并包含了多個可累加維度。外層查詢對子查詢的結果進行了進一步的聚合。基于這個查詢,可以建立以下三張異步視圖:
視圖1:包括 5 分鐘粒度聚合、可累加維度分區(dt)、日期(ts)、城市(city)、渠道(source_type)。
CREATE?MATERIALIZED?VIEW?`mv_1`
PARTITION?BY?(dt)
DISTRIBUTED?BY?HASH(`ts`)?BUCKETS?1
REFRESH?ASYNC?START(“2023-06-28?21:00:00”)?EVERY(INTERVAL?30?SECOND)?PROPERTIES?(?
"partition_refresh_number"?=?"3"?)?
AS?SELECT?
????`dt`,
????time_slice(`table_time`,?INTERVAL?5?minute,?floor)?AS?`ts`,?
????`city`,
????`source_type`,
????count(DISTINCT?`order_id`)?AS?`order_num`?
FROM?
???`base_table`?
GROUP?BY?
????`dt`,?
????`ts`,?
????`city`,?
????`source_type`;
視圖2:在視圖1的基礎上,增加了業務線(product_line)作為可累加維度。
CREATE?MATERIALIZED?VIEW?`mv_2`
PARTITION?BY?(dt)
DISTRIBUTED?BY?HASH(`ts`)?BUCKETS?1
REFRESH?ASYNC?START(“2023-06-28?21:00:00”)?EVERY(INTERVAL?30?SECOND)?PROPERTIES?(?
"partition_refresh_number"?=?"3"?)?
AS?SELECT?
????`dt`,
????time_slice(`table_time`,?INTERVAL?5?minute,?floor)?AS?`ts`,?
????`city`,
????`source_type`,
????`product_line`,
????count(DISTINCT?`order_id`)?AS?`order_num`?
FROM?
???`base_table`?
GROUP?BY?
????`dt`,?
????`ts`,?
????`city`,?
????`source_type`
????`product_line`;
然后,StarRocks 的透明加速功能會自動識別查詢條件,將查詢自動改寫為適用于這些視圖的形式,以保持查詢結果的一致性。例如:
-
Case 1:如果 WHERE 條件為空,則命中視圖1,并自動改寫查詢以使用該視圖加速。 -
Case 2:如果 WHERE 條件包含 city IN (...),則命中視圖1,并相應地改寫查詢。 -
Case 3:如果 WHERE 條件包含 product_line=?,則命中視圖2,并自動改寫查詢以使用該視圖。 -
Case 4:如果 WHERE 條件包含多個業務線查詢,不支持累加,無法命中視圖2,但如果有創建同步視圖的話,可以進行上卷加速。

通過這種方式,StarRocks 可以根據查詢條件自動選擇適當的視圖進行加速,而無需用戶手動介入,從而提高了查詢性能并簡化了用戶操作。這保證了查詢的語義一致性和性能提升。
總結與規劃
設計方案的核心在于權衡,而方案的成功關鍵在于綜合性能的提升。對于看板類查詢,其并發性非常高,但查詢模式相對固定。大多數查詢都是相似甚至重復的。因此,整體方案的思路在于在一定程度上犧牲靈活性,以保障查詢性能的極致提升。相較于基于明細直接計算,基于 StarRocks 物化視圖對業務監控看板進行加速優化的優點顯而易見:
-
單次查詢耗時降低 80%,資源消耗減少 95%; -
在相同規模的集群上,支持的查詢 QPS 提高了 100 倍。
當前,線上通過物化視圖支持了上百并發的精確去重查詢,徹底解決了 Druid 僅能支持幾十并發模糊去重的問題,相較于原有架構 QPS 提升了 10 倍。然而,方案也存在一些明顯的不足之處,包括:
-
數據鏈路相對復雜,需要人工配置視圖,維護復雜度較高,成本較高; -
異步視圖的定時刷新策略無法保證數據強一致,并且即使在沒有看板訪問時也會刷新,對計算資源有一定浪費
未來,我們會聯合社區進一步優化物化視圖,提高效率:
-
提升 BITMAP 的計算性能。這可以包括修改 StarRocks 的 BITMAP 分桶策略為 range 分桶,從而不需要對 BITMAP 的中間結果進行 shuffle 就可以得到最終結果。另外,使用 Roaring BITMAP 的 fastunion 函數來提高大量 BITMAP 的合并速度。
-
優化異步視圖在改寫計算環節的資源消耗。對于同底表相關的所有視圖,在分區數據一致性和版本一致性等方面,都存在提升的空間。
-
在易用性方面進行改進。由于看板查詢都是基于平臺配置的,自動生成的查詢 SQL,因此通過分析歷史查詢記錄,提取高頻查詢,進行物化視圖的自動創建,可以減少人工參與,從而更有利于實現技術的更大規模應用和推廣。這將使整個系統更加智能和用戶友好。
并且,社區也在研發更加高效的全局字典,通過將字典表直接緩存在 BE/CN 的內存上,以降低查詢字典時的網絡開銷,以滿足除精確去重之外的更多場景。
本文由 mdnice 多平臺發布