作者:趙煜楊 百度資深研發工程師 負責手百小程序數據產品工程架構
本次分享大綱如下:
小程序私域精細化運營能力介紹
用戶分層技術難點
用戶分層的架構和解決方案
未來規劃
小程序目前使用百度雲 Palo(Apache Doris 企業版)承載其精細化運營業務。
通過本文可以幫助大家瞭解在 Doris 中使用全局字典、BITMAP 等功能時遇到的問題、解決思路和優化方案。
1.1小程序私域精細化運營能力介紹
-
私域用戶價值不突出。比如,我是個目前有100萬用戶的開發者,想推一款奢侈品包包給用戶裏面的高收入人羣。但是我不知道這100萬人中,有多少人是高收入人羣,是很難將他們找出來的。
-
缺乏主動觸達能力,也就是觸達通路比較少。
針對這兩個問題,我們產品上提出了一個解決方案——分層運營。
分層運營分成兩部分,運營觸達和精細化人羣。
如圖所示,從上往下看,比如我現在要推一個活動,運營同學會在消息、私信、卡券和小程序內這四個通路中選擇一個進行推送。選完通路之後,就要選擇人羣。
精細化人羣篩選是基於百度的大數據平臺提供的畫像數據和行爲數據去篩選特定的人羣。
整個流程完成之後,我們會提供一個觸達效果的分析(主要包括下發量,點展和到達分析等),和一個羣體分析(對整個用戶羣的更細緻化的分析)。
從收益和價值上來看,對於開發者:
-
合理利用私域流量,提升資源價值
-
促進用戶活躍和轉換
對於整個生態:
-
提高私域池利用率和活躍度
-
激活開發者主動經營意願
-
促進生態良性循環
以上主要講了整個產品的方案,下面講一下具體的功能。
1.2 分層運營——B端視角
作爲開發者,我要如何去創建用戶分分層?
入口在小程序開發者後臺——運營中心——分層運營——分層管理——自定義篩選。點擊自定義篩選後會進入自定義篩選頁面,在這個頁面用戶可以選擇關注行爲、卡券行爲、交易行爲等維度。選擇完成之後,可以使用“預估人數”功能,實時計算一下圈選的人羣人數有多少,如果覺得所選的人羣人數比較合適就點擊“生成人羣”。生成人羣之後將會進入分層管理列表,在這裏可以發送消息或私信,還可以進行羣體分析。
1.3 分層運營——C端視角
下圖是從C端視角來分層運營的功能,這裏截取了百度App的通知和私信的樣式。
1.4 分層運營經典案例
百度App在跟汽車大師合作的時候,汽車大師選擇近一週付費且活躍用戶開展“評價送券”活動。
如圖所示,使用場景是在百度App中向用戶推送一個通知,用戶在“我的”裏面打開,填寫評價之後可以領券。
這次活動的效果是:
-
準確判斷用戶需求,活躍用戶具有較高價值,頁面打開率達9.51%
-
用戶次均使用時長提升2.5倍
-
活動帶來新增付費轉化率達17.71%
這裏面有幾個運營技巧:
-
結合實際業務場景,無中間頁跳轉折損
-
拼接消息組件,自動發券場景過渡順滑
-
場景可定期複用,節省人力成本。也就是創建完人羣之後,可以一直使用這個人羣。
-
“分享+使用”雙按鈕強勢引導
從上面分享給大家的案例可以看出,私域流量的用戶分層運營其實可以給開發者帶來運營效率和轉化效果的提升,進而促進用戶增長。
但是實現起來有很大的技術難點。
2 用戶分層難點
難點主要有四個:
首先,TB級數據,數據量特別大。我們基於畫像和行爲做用戶分層,每天的數據量大概有1T+。
第二,查詢的頻響要求極高,要求毫秒到秒級的響應時間。比如剛剛提到的“預估人數”的功能,用戶在點擊之後,我們需要在毫秒到秒的時間內,從TB級的數據中計算這個選定的人羣數量結果。
第三,計算複雜,需要動態靜態組合查詢。很多維度的數據無法進行預聚合,必須要實時計算明細數據,所以計算是很複雜的,這點後面會詳細展開。
第四,產出用戶包要求實效性高。
針對上面的四個難點,我們的方法論如下:
難點1: 壓縮存儲,降低查詢數量級,選擇使用Bitmap存儲。無論目前市場上主流的OLAP引擎有多厲害,數據量越大,查詢速度就一定越慢,所以我們要降低存儲。
難點2、3: 我們調研了當前開源主流OLAP引擎ClickHouse, Doris, Druid等,最後選擇使用基於MPP架構的OLAP引擎Doris。在性能上其實ClickHouse, Doris, Druid都差不多。但是Doris有幾個優點,第一,Doris兼容MySQL協議,學習成本非常低,基本上RD都會用。第二,Doris的運維成本很低,基本上是自動化運維,所以我們最後選擇了Doris。
難點4: 我們調研對比了spark,Doris 性能,最終也是選擇使用基於MPP架構的OLAP引擎Doris,這點在後面會詳細講。
3 用戶分層的架構和解決方案
下面我將會從架構和解決方案上講解上面這些難點是如何解決的。
3.1 分層運營架構
首先介紹一下分層運營的架構,分爲在線部分和離線部分。
在線部分分爲四層,服務層、解析層、計算層和存儲層,還有一個調度平臺和一個監控平臺。
服務層包含了權限控制(用戶權限和接口權限),分層管理(是對用戶篩選分層的增刪改查的管理),元數據管理(對頁面元素和ID Mapping的管理)和任務管理(對調度平臺任務的增刪改查的管理)
解析層主要是對DSL的解析。比如,用戶要在線預估人數。走到解析層時,首先要進行DSL解析。然後對DSL優化,比如我想找到近7天活躍的用戶和近7天不活躍的用戶求一個交集,顯然結果是0,那麼在優化層被優化掉,返還給用戶結果爲0,不會再往下走到計算引擎。優化之後會有DSL路由,這個路由的功能主要是判斷查詢的維度,路由到SQL模版進行模版的拼接。
計算引擎層主要有Spark和Doris。Spark主要用來做離線任務的計算,Doris用來做實時計算。
存儲層有MySQL(用來存用戶分層的信息),Redis(主要用來緩存),Doris(存畫像數據和行爲數據)和afs(存產出的用戶包信息)。
調度平臺用來管理離線任務的調度,監控平臺用來對整體服務穩定性監控。
離線部分主要是對需要的數據源,比如畫像、關注、行爲等數據進行ETL清洗,然後做一個全局字典,完成之後會寫入Doris。
在產出用戶包之後,會分發給小程序B端和百度統計。小程序B端會將消息推送到這些用戶的手機端;百度統計會用這個用戶包做羣體分析。
以上就是整體的架構,圖中標紅的部分是針對之前的難點做的重點改造,下面我將針對這幾個重點模塊,依次展開講解。
3.1.1 全局字典
全局字典主要是用來解決難點一——數據量大,壓縮存儲的同時保證查詢性能。
這裏大家可能會有疑問,既然用Bitmap存,爲什麼還需要全局字典?
因爲Doris 的bitmap功能基於RoaringBitmap實現的。因此當用戶id 過於離散的時候,RoaringBitmap 底層存儲結構用的是Array Container, 而沒有用到Bitmap Container。Array Container性能遠遠差於Bitmap Container,因此我們使用了全局字典將用戶id轉換成連續遞增id。
下面介紹一下全局字典的邏輯。
畫像、關注、行爲等數據源經過ETL處理之後會進入Spark中。Spark首先會加載全局字典表,這張表主要用來維護用戶ID和自增ID的映射關係,會做一次全量的加載。加載完成後會判斷用戶ID是否在這個全量的字典表中,如果存在則直接將ETL之後的數據寫入Doris,如果不存在則說明這是個新用戶,使用row_numebr()over產生一個自增ID與用戶ID做一次映射,映射完成後會寫入這張表中,同時將ETL之後的數據寫入Doris。
3.1.2 Doris
Doris之分桶策略
首先講一下分桶策略,分桶策略主要用來解決難點二——查詢頻響要求高。
之前做全局字典是要保證用戶的連續遞增,但我們發現做完全局字典之後Bitmap的查詢性能並沒有達到我們預期中的速度。後來我們發現Doris是一個分佈式的集羣,它會按照某些Key進行分桶,也就是說分桶之後用戶ID在桶內又是離散的了。
如圖中的例子所示,原始數據userid是連續的,在按照appkey和channel進行分桶之後,在桶內的userid就不是連續的了。但是不連續的話,Bitmap的性能不能很好的發揮出來。
那麼在這種情況下如何保證桶內的連續呢?
我們的方案如下:
首先我們在表中增加了hid的字段,並且讓Doris用hid進行分桶。
hid有一個算法:
hid = V/(M/N), 取整V: 插件式全局字典生成的用戶ID對應的整數M:預估的用戶總數,後續不變N:數據庫中設定的分桶數
如圖,用戶總數爲6,M=6。分桶數爲3,N=3。
這樣就將userid和hid做了對應,在用hid做分桶的時候,就可以保證桶內的連續。
Doris之用戶畫像標籤優化
以上講到的全局字典和分桶策略是通用的策略,是在做Bitmap時必須要考慮到的。但是隻考慮這兩點還不能達到性能的最優,還要結合實際的業務對業務進行優化。以下就是我們的具體業務——畫像標籤的存儲優化。
畫像標籤優化也是用來解決難點二——查詢頻響要求高。
當時有兩個方案,方案一是用tag_type和tag_value。tag_value用來記錄標籤的類型,tag_value用來記錄標籤的內容。方案二是將所有標籤放入一個寬表中。
我們選擇了方案二。因爲方案一是一個標籤對應一個用戶,如果我想選取“性別男”,地域在“北京”的用戶,就需要把兩部分用戶做一個union,有一定的計算量,會更耗時。如果用方案二,可以根據用戶常用的查詢,構建對應的物化視圖,這樣用戶查詢的時候,命中物化視圖,就可以直接取出結果,而不必去計算,可以減少耗時。
在使用Doris的時候,大家要儘量命中前綴索引和物化視圖,這樣會大大提升查詢效率。
Doris之動靜組合查詢
動靜組合查詢主要是解決難點三——計算複雜。
靜態查詢是用戶維度是固定的,可以進行預聚合的。比如“男性用戶”,這就是一個固定的羣體,無論怎麼查這部分用戶是不會變的。動態查詢是偏向一些行爲的,會根據用戶的不同而不同。比如“近30天收藏超過3次的用戶”,或“近30天收藏超過4次的用戶”,這種查詢無法進行預聚合,所以稱爲動態的查詢。
小程序用戶分層相比於同類型的用戶分層功能,增加了用戶行爲篩選(這也是小程序產品的特點之一),比如近30天用戶支付訂單超過30元的男性,20~30歲用戶。其中近30天用戶支付訂單超過30元用戶是Bitmap表無法記錄的,也不能提前預計算好,只能在線去算。
這裏的難點是,如何進行非Bitmap表和Bitmap表的交併補集運算?
結合上面的例子,我們的解決方法是將查詢查分成四步。
Step1 先查20-30歲的男性用戶,這部分直接查Bitmap表就行。
Step2 查詢近30天用戶支付訂單超過30元用戶,這步需要去查行爲表獲取用戶ID。
Step3 將用戶ID跟在線Bitmap的轉化,Doris提供了一個bitmap_union(to_bitmap(id)) 功能,可以在線將用戶id 轉換成bitmap。
Step4 是將Step1和Step3的結果求交集。
這裏的重點是Step3,Doris提供了to_bitmap的功能,幫我們解決了Bitmap表和非Bitmap表的聯合查詢的問題。
3.1.3 如何快速產出用戶包
快速產出用戶包是爲了解決難點四——產出用戶包要求時效性高。
同樣有兩個方案,方案一是用調度平臺+Spark。分成三步,首先產出分層用戶cuid,然後產出用戶uid,最後回調更新。方案二是用調度平臺+solo,執行DAG圖是用solo產出cuid、uid和回調,這裏的solo是百度雲提供的Pingo單機執行引擎,類似一個虛擬機。
方案一由於spark自身yarn調度耗時,加上如何隊列資源緊張需要延遲等待等原因,即使產出0個用戶,也需要30分鐘才能跑完。方案二我們利用了Doris的SELECT INTO OUTFILE產出結果導出功能:查出的用戶直接導出到afs上,百萬級用戶產出小於3分鐘。
最終我們選擇了方案二,因爲Doris相比於spark導出結果到afs更快。
3.2 收益
Doris的用戶存儲方案還是非常有效的,整體方案的收益如下圖所示:
4 未來規劃
首先在產品上:
-
豐富分層應用場景, 拓展強關係維度, 豐富觸達形式
-
探索分層和商業的結合模式
在技術上:
-
時效性:交易,訂單, 關注等行爲實時化
-
豐富性: 接入更多的用戶畫像標籤及行爲
-
通用性: 全局字典插件化, 通用到各個業務
期待你的加入
百度開發者中心已開啓徵稿模式,歡迎開發者登錄developer.baidu.com進行投稿,優質文章將獲得豐厚獎勵和推廣資源。