【51CTO.com原創稿件】想做營銷活動,如何找到目標人群及用戶特征?人群的篩選通常離不開用戶畫像。
圖片來自 Pexels
用戶畫像就是根據用戶特征、業務場景和用戶行為等信息,構建一個標簽化的用戶模型。
比如消費者用戶畫像分為屬性和行為標簽兩類。這兩類標簽,一個是固定的,一個是可變的。
固定的屬性標簽基本就是買家的性別,年齡段,會員等級,消費水平,購買力等。而可變的行為標簽基本包括買家的瀏覽,加購物車,購買等行為。
通過多年的建設,蘇寧構建了完整的用戶標簽體系,覆蓋零售、金融、體育等多個產業。
同時搭建了標簽服務平臺,通過開放豐富的標簽數據能力,為廣告、推薦等提供智能化的標簽中臺服務能力。
隨著數據的日益增多,如何對 6 億+用戶千億級別的標簽數據進行秒級用戶畫像?
本文將帶來用戶畫像技術的新發展和架構實踐,介紹基于 ClickHouse 定制開發的標簽平臺,真正做到海量標簽數據的快速導入和秒級用戶畫像查詢分析,提供一整套從前期人群篩選到后期的營銷策略優化的標簽體系。
業務場景介紹
“雙 11”到了,假設需要發放 1000 萬張家電類優惠券,那我們首先需要根據標簽篩選出符合條件的人群,人數大約為 1000 萬左右,然后對選擇的人群進行畫像分析,看是否符合預期的特征。
如果人群符合特征,系統將一鍵生成進行營銷的人群包(userid 列表),自動化發布和營銷。
圖 1:業務流程
預估人數
用戶選擇標簽及標簽之間的交并差關系,圈選出符合條件的人群,實時預估出人群的數量。
比如選擇:
圖 2:創建人群
上圖的標簽選擇的含義為:“用戶年齡范圍為 25-36 歲”并且為“智能家居特征”的人群,排除最近 30 天消費小于 10 元的人群。
表示為集合運算的公式為:
- { {用戶年齡 25-36} ∩ {智能家居人群} } - {30天消費小于10元}
技術難點有:
- 人群包的個數多。
- 每個人群包的用戶基數較大。
- 系統實時輸出計算結果,難度大。
畫像分析
當篩選出用戶數與規劃的消費券的數量匹配時,需要對人群進行特征分析,看看人群是否符合特征要求。
用戶畫像表的結構舉例如下:
將篩選出的人群包與用戶畫像表進行關聯,詳細分析關聯出的畫像特征。也可以進一步對畫像特征進行一些歷史數據分析。
我們之前的解決方案是將用戶標簽存儲在 ElasticSearch 的大寬表中的。大寬表的結構是:一個用戶下掛一堆 tag 的表結構。
在向大寬表插入數據時,需要等待業務的數據都準備好后,才能跑關聯表操作,然后將關聯的結果插入到 ES。
經常遇到的情況是:某個業務方的任務延遲,導致插入 ES 的關聯任務無法執行,運營人員無法及時使用最新的畫像數據。
在 ES 中修改文檔結構是比較重的操作,修改或者刪除標簽比較耗時,ES 的多維聚合性能比較差,ES 的 DSL 語法對研發人員不太友好,所以我們將標簽存儲引擎從 ES 替換為 ClickHouse。
ClickHouse 是近年來備受關注的開源列式數據庫,主要用于數據分析(OLAP)領域。憑借優異的查詢性能,受到業界的青睞,各個大廠紛紛跟進大規模使用它。
蘇寧大數據已將 ClickHouse 引入并改造,封裝成豐富的 Bitmap 接口,用來支撐標簽平臺的存儲及分析。
ClickHouse 集成 Bitmap
我們在 ClickHouse 中集成了 RoaringBitmap,實現了 Bitmap 計算功能集,并貢獻給開源社區。
對 userid 進行位圖方式的壓縮存儲,將人群包的交并差計算交給高效率的位圖函數,這樣既省空間又可以提高查詢速度。
圖 3:ClickHouse 集成 Bitmap
圍繞 Bitmap 對象實現了一套完善的計算函數。Bitmap 對象有兩種構建方式,一種是從聚合函數 groupBitmap 構建,另一種是從 Array 對象構建,也可以將 Bitmap 對象轉換為 Array 對象。
ClickHouse 的 Array 類型有大量的函數集,這樣可以更加方便的加工數據。
上圖的中間部分是 Bitmap 的計算函數集,有位運算函數、聚合運算函數、求值類運算函數,比較豐富。
基于 ClickHouse 的新架構
架構介紹
架構圖如下:
圖 4:標簽架構
ClickHouse Manager 是我們自研的 ClickHouse 管理平臺,負責集群管理、元數據管理和節點負載協調。
Spark 任務負責標簽數據的生成和導入,當某個業務方的任務跑完后,會立刻調用 tag-generate 生成標簽數據文件,存放到 HDFS,然后在 ClickHouse 上執行從 HDFS 導入到 ClickHouse 的 SQL 語句,這樣就完成了標簽的生產工作。
標簽生產是并發跑的,假設某個業務方的數據沒有準備好,不影響其他業務的標簽生產。
用戶畫像平臺通過 Proxy 從 ClickHouse 集群查詢標簽數據。在查詢前,需要將查詢表達式轉換為 SQL,我們對這塊邏輯做了一個封裝,提供一個通用的轉換模塊,叫做:to-ch-sql。
業務層基本上不用修改就可以查詢 ClickHouse 了。
標簽數據表的基本結構
相對于 ElasticSearch 的存儲結構,我們將標簽存儲做了一個行轉列存儲。每個標簽對應一個 Bitmap 對象。
Bitmap 對象中存儲 userid 集合:
- CREATE TABLE ch_label_string
- (
- labelname String, --標簽名稱
- labelvalue String, --標簽值
- uv AggregateFunction( groupBitmap, UInt64 ) --userid集合
- )
- ENGINE = AggregatingMergeTree()
- PARTITION BY labelname
- ORDER BY (labelname, labelvalue)
- SETTINGS index_granularity = 128;
uv 字段為 Bitmap 類型的字段,將整形的 userid 存入,每個 userid 用一個 bit 位表示。
主鍵索引(index_granularity)默認為 8192,修改為 128 或者其他數值,由于 Bitmap 占用的存儲空間比較大,修改為小數值,以減少稀疏索引的讀放大問題。
根據標簽值的數據類型劃分為四種類型的表:
- String
- Integer
- Double
- Date
標簽名稱作為 Partition。通過這樣的設計,增加或者刪除標簽數據都比較方便,只需要修改 Partition 的數據就可以了。Partition 的管理有相應的 SQL 語句,操作比較方便。
Userid 分片存儲
在標簽數據導入時,按照 userid 分片導入,每臺機器僅存儲對應 userid 的標簽數據。
每臺機器分別導入分片后的標簽數據,實現了數據并行導入。在我們的環境上單機導入性能在 150 萬條/秒左右。
在根據標簽篩選人群時,SQL 僅需要在單個 shard 上執行,中間結果不需要返回給查詢節點。
在執行“預估人數”計算時,優勢特別明顯:每個 shard 僅需要返回符合條件的人數,在查詢節點做 sum 操作,然后將 sum 結果返回給客戶端。充分挖掘了 ClickHouse 分布式并行計算的能力。
查詢流程
采用 with 語句進行計算出人群包的 Bitmap 對象,然后用 Bitmap 函數進行交并差的計算。
當需要計算的標簽比較多時,標簽查詢的 SQL 比較復雜,將標簽查詢 SQL 包裝到分布式代理表的 SQL 中,分布式代理表本身不存儲數據,通過代理表標識到哪些節點上查詢,分布式代理表所標識的節點上執行標簽查詢 SQL。
然后在分布式代理表上匯總查詢結果。通過 ClickHouse 分布式表的自身特性,實現了標簽查詢的 colocate 機制。
圖 5:查詢流程
示例 SQL 如下:
- -- 本地查詢代理
- CREATE TABLE ch_agent_user
- (
- agentname String
- )
- ENGINE = MergeTree()
- PARTITION BY agentname
- ORDER BY (agentname)
- SETTINGS index_granularity = 8192;
- -- 分布式代理表
- CREATE TABLE ch_agent_dist_user AS ch_agent_user
- ENGINE = Distributed('cluster_test', 'test', 'ch_agent_user', cityHash64(agentname))
- -- 查詢用戶數
- SELECT sum(user_number) AS user_number
- FROM ch_agent_dist_user
- RIGHT JOIN
- (
- WITH
- (
- SELECT groupBitmapState(userid) AS users0
- FROM ch_label_string
- WHERE labelname = 'T'
- ) AS users0
- SELECT
- 'agent' AS agentname,
- bitmapCardinality(users0) AS user_number
- ) USING (agentname) settings enable_scalar_subquery_optimization = 0;
ch_agent_user 表本身不存儲數據,當與 with 語句進行 right join 關聯查詢時,由于是右關聯查詢,查詢結果以 with 語句的結果集為準。
各個節點的查詢結果返回給查詢節點,查詢節點進行匯總計算。參數 enable_scalar_subquery_optimization = 0 表示 with 語句的查詢結果不做優化,每個節點都需要執行。
默認情況,在 ClickHouse 中 with 語句的結果作為標量進行緩存,會將查詢節點的標量分發到其他服務器,當發現已經存在標量時,就不會在本地節點執行 with 語句。
我們期望 with 語句在每個節點都執行,所以將這個參數設置為 0。
用戶畫像
用戶畫像對性能要求比較高,查詢平均響應時間不能大于 5 秒。用戶在界面上任意圈選人群,然后實時對圈選后的人群進行畫像分析。
用戶畫像技術進行了三次架構重構:
①V1:大寬表模式
最早的方案是創建一張 userid 為主鍵的畫像表,表的其他字段為畫像的特征字段,將圈選的人群與畫像表進行 in 操作,然后 group by 操作。
這種設計帶來兩個比較嚴重的問題:
- 當增加或者刪除特征字段時,畫像表的表結構需要修改。
- 當圈選的人群數量比較大時,涉及到大記錄集的 group by 運算,性能差。
②V2:Bitmap 模式
將一個特征值下的 userid 集合做為 Bitmap 對象存儲到一條記錄中,一條記錄的內容如下:
用戶圈選的人群 Bitmap 對象與畫像表的 Bitmap 對象進行與(AND)操作,返回圈選人群的畫像信息。
通過這樣設計,基本上滿足了性能要求,平均時間小于 5 秒,但是一些大的人群包,畫像的性能還是差,在 10 秒左右。
畫像表的記錄數據量不大,但畫像表的 Bitmap 字段在計算時需要從磁盤上反序列化出來。有的 Bitmap 對象占用幾百兆的空間,導致了性能的下降。
③V3:Join 表引擎模式
ClickHouse 的 Join 表引擎可以將數據常駐到內存。當插入數據時,數據先寫入內存,然后刷到磁盤文件,系統重啟時,自動把數據加載回內存。Join 表引擎可以說是常駐內存的帶持久化功能的表。
我們把畫像表的數據保存到 Join 表引擎,畫像表的 Bitmap 字段就常駐內存了,當圈選的人群 Bitmap 對象進行與(AND)操作時,兩個內存中已經加載的 Bitmap 對象之間的計算就非常快。
通過這次優化平均查詢時間優化到 1 到 2 秒,千萬級人群畫像分析不超過 5 秒。
總結
通過 ClickHouse 集成 Bitmap 功能,以及 Join 表引擎的應用,對架構進行了一系列優化后,極大的提升了標簽平臺的數據分析能力。
新的架構主要有以下優勢:
- 標簽數據可以并行構建,加快標簽數據生產速度。
- HDFS 文件并發導入 ClickHouse,加快標簽數據的就緒速度。
- 查詢請求平均響應時長在 2 秒以下,復雜查詢在 5 秒以下。
- 支持標簽數據準實時更新。
- 標簽表達式和查詢 SQL 對用戶來說比較友好,提升系統的易維護性。
- 相對于 ElasticSearch 的配置,可以節約一半硬件資源。
未來規劃:
- 目前 ClickHouse 采用 RoaringBitmap 的 32 位版本,準備增加 64 位版本。
- ClickHouse 查詢的并發性較低,增加更加智能的 Cache 層。
- 支持 ClickHouse 數據文件離線生成,進一步提示標簽的就緒速度。
參考:
- ClickHouse 官網:https://clickhouse.tech/
- ClickHouse 中文社區:http://www.clickhouse.com.cn/
- Bitmap PR:https://github.com/ClickHouse/ClickHouse/pull/4207
作者:楊兆輝
簡介:蘇寧科技集團大數據中心高級架構師,ClickHouse Contributor。在 OLAP 領域、大規模分布式計算領域有著深厚的技術積累,目前負責數據中臺、標簽平臺相關的架構工作。
編輯:陶家龍
征稿:有投稿、尋求報道意向技術人請聯絡 editor@51cto.com
【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】