貨拉拉是國內領先的同城貨運數字化平台,成立於 2013 年。截⾄2025 年 4⽉ ,貨拉拉業務覆蓋全球 14 個市場 ,400+ 城市 ,其中中國內地總共覆蓋 363 座城市 ,⽉活司機達 120 萬 ,⽉活⽤户達 1400 萬, 並在全球設有 6 個數據中⼼。作為共享經濟模式的代表企業 ,貨拉拉通過移動互聯⽹技術整合社會運⼒資源 ,為⽤户提供即時貨運、企業物流、搬家服務等多元化解決⽅案。
在龐⼤的業務規模下 ,構建完善的⽤户畫像平台成為實現精細化運營的重要基礎 ,可以有效提升運營效率和⽤户體驗。
畫像服務背景與架構
目前貨拉拉的畫像平台已深度應用於多個核心業務場景,各業務場景對標籤的使⽤⽅式和時效性要求各不相同。從 22 年到 25 年,用户呈現穩定增長趨勢,業務對服務的壓力也是逐年遞增,累計接入了 300+ 業務,3,000+ 個標籤以及 5 萬多人羣。
01 應用架構
上圖是貨拉拉畫像平台整體架構圖,Apache Doris 作為統一查詢引擎,為人羣畫像提供高效的分析能力。該平台通過架構分層實現了業務需求與技術能力的精準匹配。其中有兩個核心模塊:
-
API 層(接⼝服務層):基於重點使⽤場景和對接系統搭建的畫像接口服務 ,從⽽精準地⽣成用户畫像 ,給到對接業務⽅使⽤。其中包括:
- persona-api:⾯向⾼併發查詢場景
- persona-analysis-api:承接分析計算與推送作業請求
- persona-web-api:⽀持管理後台的 Web 服務
-
BE 層(計算引擎層) :建造基於大數據體系的用户標籤鏈路的畫像管理平台 ,⽤於在特定業務形態下描述業務主體;其中包括:
- persona-task:執⾏分佈式計算作業 ,⽀持橫向擴展
- persona-scheduler :調度⼈羣計算任務
兩大模塊協同工作,共同支撐業務方的精細化運營需求。
02 畫像平台計算引擎演進
在核心計算引擎的選型與演進過程中,技術團隊經歷了三個主要階段。
- 第一階段,受限於成本因素,採用了三方服務結合 Impala+KUDU 的架構作為用户畫像計算引擎。該架構在實際應用中暴露出諸多問題:單次人羣計算耗時 10 分鐘以上,高峯期甚至超過 30 分鐘;同時,數據導入耗時較長,經常超時。此外,該架構存在橫向擴展困難、複雜查詢效率偏低及運維複雜等不足。
- 第二階段,為尋求改進,我們嘗試引入 Elasticsearch,雖在一定程度上有所緩解,但仍面臨開發成本高、語法複雜、多維分析能力不足等挑戰,且在動態擴展和複雜查詢方面未實現根本性改善。尤其在處理人羣間存在依賴關係的特殊業務場景時,Elasticsearch 架構難以有效支持。
-
基於此,我們最終轉向採用 Doris 作為核心計算引擎。選擇 Doris 的關鍵因素如下:
- 性能優越:基於 MPP 架構、具備向量化引擎和先進的優化器能力,查詢性能優秀。
- 社區資源豐富:Doris 擁有活躍的社區支持以及豐富的文檔資料,自行搭建遇到卡點時,可向社區幫助尋求專業的指導與幫助。
- 支持多種數據類型:畫像場景可以使用 BITMAP 實現高效的交併集運算,成為支撐多樣化標籤類型與人羣分析業務的技術基礎。
- 支持多種數據模型:針對多維複雜的人羣畫像,可以使用不同的數據模型支撐各種標籤類型與人羣業務。
自引入 Doris 後,系統穩定性得到了顯著提升,至今未出現過重大穩定性問題,整體鏈路的時效性與可靠性均實現了根本性的優化,具體如下:
- 在計算效率方面,單個人羣的計算能夠實現秒級響應,即使在高峯期,響應時間也保持在 1 分鐘以內。相比之下,Impala 架構下該計算過程通常需要 10 至 30 分鐘。採用 Doris 後,計算效率提升了近 30 倍。
- 在數據導入方面,Doris 同樣表現出色。在處理 4 億行 200 列的單表數據時,Doris 可在 30 分鐘內完成導入操作。而在同等條件下,Impala 架構則需要 90 分鐘以上,Doris 數據導入效率是 Impala 架構的 3 倍。
數據模型設計與異構查詢實現
01 核心挑戰
首先是用户畫像標籤存儲的挑戰。面對 3000+ 標籤的體系,我們發現標籤數據天然存在三個維度的分裂特徵:業務屬性(行為/屬性/地理等)、聚合粒度(明細/聚合/人羣)、更新時效(離線/近實時/實時)。不同業務屬性的數據在更新頻率、查詢模式、存儲密度上差異巨大。
此外,用户使用畫像標籤的時候,對人羣標籤/明細標籤/聚合標籤等概念不清晰,只會進行簡單的拖拉拽拼接規則。因此在標籤使用場景,如何選擇存儲模型成為了核心問題。若使用寬表,將面臨動態更新以及列拓展的挑戰;而高表則面臨複雜查詢與嵌套邏輯的挑戰。
02 存儲模型設計
面對複雜的標籤管理,我們採用了基於 Apache Doris 分而治之的存儲方案,具體分為三類模型來進行協同工作。
標籤寬表
- 存儲低頻更新的標籤。
- 存儲無法用高表存儲的密集標籤。
- 利用 Doris 的列式存儲技術,利用索引、物化視圖等優化手段,支持高效的多維分析。
標籤寬表建表 SQL 參考:
CREATE TABLE wide_table (
user_id varchar(1000) NULL COMMENT "",
age bigint(20) REPLACE_IF_NOT_NULL NULL COMMENT "",
height bigint(20) REPLACE_IF_NOT_NULL NULL COMMENT "",
......) ENGINE=OLAP AGGREGATE KEY( user_id ) COMMENT "OLAP"
DISTRIBUTED BY HASH( user_id ) BUCKETS 40
PROPERTIES ( ... )
標籤高表
- 存儲高頻更新的稀疏標籤。
- 支持秒級/分鐘級數據更新,標籤新增的場景下,可以規避寬表頻繁 ALTER TABLE 導致的鎖表問題。
- 多個標籤的位圖交併計算,並且支持毫秒級響應。
標籤高表建表 SQL 參考:
CREATE TABLE high_table (
tag varchar(45) NULL COMMENT "標籤名",
tag_value varchar(45) NULL COMMENT "標籤值",
time datetime NOT NULL COMMENT "數據灌入時間",
user_ids bitmap BITMAP_UNION NULL COMMENT "用户集"
) ENGINE=OLAP AGGREGATE KEY( tag , tag_value , time ) COMMENT "OLAP"
DISTRIBUTED BY HASH( tag ) BUCKETS 128
PROPERTIES ( ... )
人羣位圖表
- 用於存儲標籤規則圈選出來的人羣結果。
- 用户 ID 集合使用 RoaringBitmap 壓縮,降低存儲成本。
- 支持人羣依賴計算,避免表數量膨脹問題,⽀持⼈羣營銷實驗業務。
人羣位圖表建表 SQL 參考:
CREATE TABLE routine_segmentation_bitmap (
time datetime NOT NULL COMMENT "數據灌入時間",
seg_name varchar(45) NULL COMMENT "標籤值",
user_ids bitmap BITMAP_UNION NULL COMMENT "人羣ID集合"
) ENGINE=OLAP AGGREGATE KEY( time , seg_name )
COMMENT "OLAP" PARTITION BY RANGE(`time`) (...)DISTRIBUTED BY HASH( seg_name )
BUCKETS 128
PROPERTIES (..., "dynamic_partition.enable" = "true", ...);
03 人羣圈選與異構組合查詢
基於上述提到了三種存儲模型,我們構建了以位圖計算為核心的異構組合計算體系,並將其作用於整個人羣圈選的場景,實現寬表、高表及人羣表這三類存儲模型之間的無縫聯動。
- 第一步:將所有單一標籤或子查詢的結果都處理成 Bitmap 位圖,處理寬表的標籤使用
TO_BITMAP聚合多列 ID 結果,處理標籤高表和人羣表則直接使用預聚合的 Bitmap,加速邏輯複用。 - 第二步,通過
UNION ALL整合寬表/高表/人羣表三種數據源的 Bitmap,並使用外層的BITMAP_INTERSECT/BITMAP_UNION實現跨模型交併集運算(AND/OR 邏輯)。 - 第三步,由於
BITMAP_INTERSECT/BITMAP_UNION的結果也是 Bitmap,所以可以用同樣的處理方式,遞歸整合子查詢的查詢結果,並支持直接導出或對接業務系統。
該實現方法具備三大核心優勢:
- 靈活拓展:動態嵌套子查詢支持無限層級規則(如 “A 且(B 或 C)且(D 且(E 或 F))” ) ,靜態屬性、動態行為以及預計算人羣等複雜的標籤規則皆可無縫嵌入。
- 資源節省:複用人羣表(預計算人羣)的數據從而大幅減輕計算壓力,並允許不同人羣間相互依賴進行計算;人羣計算結果使用 RoaringBitmap 存儲,不需要額外新增表。
- 業務友好:支持人羣+標籤的複雜混合嵌套查詢,用户無需技術基礎,僅通過簡單的拖拽標籤條件即可高效圈定目標用户羣體,適用於營銷等多種場景。
查詢 SQL 示例:
SELECT BITMAP_INTERSECT(b) FROM (
-- Layer1: 寬表條件A
SELECT TO_BITMAP(...) AS b FROM 標籤寬表 WHERE 條件A
UNION ALL
-- Layer1: 高表條件B
SELECT user_ids AS b FROM 標籤高表 WHERE 條件B
UNION ALL
-- Layer1: 人羣條件C
SELECT user_ids AS b FROM 人羣表 WHERE 條件C
UNION ALL
-- Layer2: 嵌套子查詢(新條件D/E/F)
SELECT BITMAP_INTERSECT(b) FROM (
SELECT TO_BITMAP(...) AS b FROM 標籤寬表 WHERE 條件D -- 新寬表條件
UNION ALL
SELECT user_ids AS b FROM 標籤高表 WHERE 條件E -- 新高表條件
UNION ALL
SELECT BITMAP_INTERSECT(...) AS b FROM 人羣表 WHERE 條件F -- 新人羣條件
)
) t;
04 數據導入
貨拉拉畫像服務的實時標籤/人羣點查主要採用 HBase 與 Redis 相結合的方式,Doris 主要承擔人羣圈選、人羣洞察、行為分析等任務。實時和近實時標籤寫入 Doris 則通過 Flink 完成,離線導入依賴 Doris 的 Broker Load 功能。對應的數據導⼊⽅式和應⽤場景如下:
實時/近實時標籤
定義:秒級/小時級更新的動態數據(如點擊、登錄事件)。
數據源:Kafka 日誌、API 埋點、雲文件存儲。
處理方式:
- 秒級/分鐘級標籤:Flink -> Doris / Hbase。
- 小時級標籤:雲文件存儲 -> BrokerLoad。
場景:用户行為分析,用户實時人羣。
離線標籤
定義:T+1 更新的歷史數據(如年齡、歷史訂單)。
數據源:Hive。
處理方式:
- 數據量:3000+ 標籤,4 億+用户總量。
- 定時調度:BrokerLoad。
- 調優手段:寬表導入拆分多表和多個 BrokerLoad 任務。數據量少的稀疏標籤使用高表導入。
- 導入效率:200+ 標籤寬表 4 億行+導入 30min 以內,高表標籤導入 5min 以內。
場景:用户人羣圈選、用户畫像分析。
特別值得注意的是,大規模數據導入時,建議拆分多個表並行導入以提升效率,例如在早期 1.2.4 版本集羣測試中,單個包含 200 餘標籤、四億行記錄的寬表導入耗時超過 1.5 小時,而拆分為 6 個任務並行導入後,總耗時縮短至約 20 分鐘,效率提升近 5 倍。
貨拉拉畫像工程查詢優化實踐
01 DSL 與 SQL 優化
首先是離線人羣包圈選的流程,主要分為三步:【運營通過平台進行多規則拼接,前端完成 DSL 構建】-【DSL 經過服務後端優化】-【最終將業務規則自動轉化為高效 SQL】。DSL 優化的目的是提前排除冗餘計算,從而將優化後的 DSL 直接翻譯為高效易用的 SQL。
-
為什麼需要優化?當前異構查詢 SQL 痛點:
- 部分標籤在業務邏輯上可以合併 ,引擎側沒有覆蓋識別;
- 多層聚合導致冗餘掃描:複雜嵌套的場景下,嵌套的
UNION ALL和BITMAP_INTERSECT導致執行計劃層級膨脹,導致冗餘的掃描。 - 穩定性:高峯期人羣計算時,內存佔用高、網絡傳輸量大,高內存開銷影響集羣穩定性。
-
如何實現優化?DSL 優化:
- 條件合併(染色):將同類標記的標籤條件合併為同個子查詢。
- 結構扁平化(剪枝):去除冗餘的 AND/OR 邏輯節點
將同類合併操作後的 DSL 轉為 SQL,原本查詢 3 次寬表讀取了 3 個 BITMAP 進行合併計算,優化後統一成 1 次寬表查詢和 1 次 BITMAP 讀取,減少了60% 的冗餘讀取。參考下圖,DSL 的每一個圓圈對應一個 BITMAP 查詢:
關於 EXPLAIN 指標優化前後的具體表現,我們在此也分享一則參考示例,詳情如下:
-- 優化前
SELECT BITMAP_INTERSECT(b) AS result
FROM (
SELECT BITMAP_INTERSECT(b) AS b
FROM (
SELECT user_bitmap as b FROM user_bitmap WHERE group = ‘A’
UNION ALL
SELECT TO_BITMAP(id) AS b FROM wide_table WHERE city = '東莞'
UNION ALL
SELECT TO_BITMAP(id) AS b FROM wide_table WHERE sex = '男'
) t1 ) t2;
-- 優化後
SELECT BITMAP_INTERSECT(b) AS result
FROM ( SELECT user_bitmap as b FROM user_bitmap WHERE group = ‘A’ UNION ALL
SELECT TO_BITMAP(id) AS b FROM wide_table WHERE city = '東莞' and sex = ‘男’
) t1 ) t2;
在業務圈選邏輯不變的情況下,通過將優化前 BITMAP 子查詢合併到 WHERE 條件中以避免重複掃描表,同時減少 BITMAP 子查詢及數據分片合併次數,進而減少聚合層級實現結構扁平化,降低人羣計算時的內存峯值。
在業務高峯期針對大規模人羣展開計算的場景下,此優化措施能夠有效減少 30~50% 的內存開銷。與此同時,JVM 的堆內存使用峯值從原本的 60% 降至 20% 。
02 人羣位圖表讀取優化
人羣位圖結果集通過 BITMAP 存儲於人羣表中,讀取人羣位圖數據主要用於人羣圈選、人羣分析,並將整個結果集推送至下游。
讀取位圖數據的方案有三種:
- 第一種方案,直接使用 Doris 自帶的
bitmap_to_string函數,該函數可將位圖轉化為逗號分隔的字符串,客户端按此字符串解析出用户列表。但是當面對大型位圖時,解析難度較大,且數據體積會大幅膨脹。優點是簡單易用,適合測試和導出場景。 - 第二種方案,採用
explore與lateral view先將整個位圖展開,再構建服務端流處理邏輯。不過,Doris 需將位圖展開成用户列表並緩存於服務器,這會給服務端帶來壓力,尤其在高峯期,人羣計算任務繁重時更為明顯。此外,畫像工程側需維護流處理邏輯,開發維護成本較高。該方案適合人羣多表的關聯分析。 - 第三種方案,也是較為推薦的方案,直接將位圖的二進制數據讀取至服務端內存,再進行反序列化。服務端設置
return_object_data_as_binary=true,即可直接讀取位圖的二進制數據,畫像服務端可基於 Doris 源碼中的位圖協議進行反序列化。此方式僅需傳輸位圖的原始二進制數據,內存佔用和開銷較低。開發成本初期較高,但後期維護方便且穩定。此讀取方式適用於人羣圈選場景,將位圖全量讀取至服務內存後,高峯期每分鐘可輕鬆處理幾十甚至上百個人羣。
總結與規劃
貨拉拉自引入 Apache Doris 構建用户畫像系統以來,收益十分顯著:業務查詢效率提升近 30 倍,數據導入速度是 Impala+KUDU 的 3 倍,內存開銷降低 30%-50%,系統穩定性大幅提升,滿足了畫像場景數千個標籤的精細化運營需求,同時通過可視化標籤篩選,降低業務分析的操作門檻。
後續,貨拉拉將重點投入以下兩個方面:
- 接入畫像實時業務:當前貨拉拉畫像服務的實時標籤/人羣點查主要使用 Hbase 和 Redis,基於穩定性和遷移升級成本的考慮,Doris 主要承擔人羣圈選、人羣洞察、行為分析等作業。未來規劃使用 Doris 高版本的架構,承擔大部分高併發的實時點查流量,提升人貨匹配效率和體驗。
- 引入湖倉一體架構:使用 Doris + 數據湖,在存儲架構方面,嘗試落地數據湖解決方案,畫像平台將打通其他數據應用平台、實現超大規模數據的分析。