Clickhouse-Bitmap 實現用戶畫像中海量用戶的圈選

1. 圈客實現

我們知道用戶畫像最主要的功能就是實現分客營銷分客分析的功能,我們也介紹過可以使用畫像寬表可以便捷的創建想要的客羣,具體實現思路將 就是將 Hive 表(大數據平臺開發的標籤寬表)中的數據同步到 ClickHouse 中進行緩存,人羣圈選引擎可以從 ClickHouse 中直接查詢到滿足條件的 UserId 然乎進行後續的業務開展。

比如江蘇省中年男性用戶,所有的標籤拼接到一張數據表中並構建出一張寬表,上述圈選 SQL 語句如下語句。

SELECT user_id FROM  dm_demo.userprofile_wide_table  WHERE   sex = '男'   AND province = '江蘇省';

2.Bitmap 圈客實現

我們可以看出,使用了畫像寬表圈人的邏輯其實就是從明細數據中找到滿足條件的用戶並最終構建人羣,也就是我們篩了多少標籤就在對應的表後就對應的條件,如果海量標籤的話,這個也會存在一定效率低下,這裏我們可以引進 bitmap 的實現方式來優化,使用 BitMap 進行圈人的話,會對用戶進行預聚合,在人羣圈選時直接使用聚合後的結果進行計算。具體做法是首先將指定標籤值下的所有用戶聚合後生成 BitMap,然後在基於這些做好的 BitMap 執行交集、並集、差集等操作來實現人羣篩選。從而解決圈選速度的問題,來大大提升圈客的效率。下圖是實現具體的 bitmap 包的圖例。

bitmap(位圖)是一種利用比特位來進行數據存儲的結構,BitMap 特殊的數據結構決定了它比較適合做用戶聚合並應用到人羣圈選場景下。具體實現是 BitMap 底層構建了一個 bit 數組,bit 每一位只能存儲 1 或者 0,其中數組的索引值映射到 UserId,當前索引上的數字是 1 的時候代表對應的 UserId 存在,是 0 的時候代表 UserId 不存在。圖 5-9 展示了 BitMap 存儲 UserId 的基本邏輯,UserId 不再是一個具體數字而是映射到位數組的索引值上面,藉助這一特點可以實現大量 UserId 數字的壓縮、去重、排序和判存。

這個熱時候,將大量的用戶 Id 寫入 BitMap 時,因爲相同的 UserId 所對應的索引位置一樣,可以自動實現人羣用戶 Id 的去重;bit 數組索引是天然有序的,用戶 Id 寫入 BitMap 可以實現便捷排序;判存是判斷 UserId 是否在人羣中,通過判斷 bit 數組指定索引位置的數值是否爲 1 便可以快速判斷出 UserId 是否存在。BitMap 以上特點都非常適合存儲人羣數據,也決定了其在畫像平臺的廣泛使用。

3.Hive 實現 Bitmap

我們可以在 clickhouse 裏轉化爲 bitmap,但是有點時候也需要在 hive 存一份備份或者而其他使用,所以我們可以在 hive 裏直接轉化好,Hive 表數據轉爲 BitMap 依賴開源工具包 hive-bitmap-udf.jar,這是個外包,需要導入,其中 UDF 函數 to_bitmap 可以將 UserId 列表轉換爲 BitMap 存儲到 Hive 表中。工具包中還包含常用的 UDF 函數:bitmap_count、bitmap_and 和 bitmap_or 等,可以便捷地對 BitMap 進行各類操作。Hive 表中的 BitMap 數據經由 Spark 等大數據引擎批量處理後寫入 ClickHouse 表中。

add jar hdfs://userprofile-master:9000/hive-bitmap-udf.jar;
create temporary function to_bitmap as 'com.hive.bitmap.udf.tobitmapudaf';
insert overwrite table userprofile_demo.gender_label_bitmap 
partition(p_date = '2022-08-01')
select
sex
,to_bitmap(user_id)
from
dm_demo.gender_label
where
p_date = '2022-08-01'
group by sex;

bitmap 數據存儲,CK 本身並沒有這種類型,而是通過參數化的數據類型 AggregateFunction(name, types_of_arguments…) 來實現,建表示例:

CREATE TABLE userprofile_demo.tag(
    `code` String,
        `values` String,
    `offset_bitmap` AggregateFunction(groupBitmap, UInt64)
  )
  ENGINE = ReplicatedMergeTree
('/clickhouse/tables/cdp/{shard}/group_1',
  '{replica}'
  )
PARTITION BY(code)
 ORDER BY(code)
;

這樣,源數據和加工後的 bitmap 都可以存儲在 CK 中。

4. 混合模式圈客實現

雖然我們用 bitmap 來優化圈客,但是並不是所有的畫像標籤都適合轉換爲 BitMap,只有標籤值可枚舉的,並且數量有限的標籤才適合轉換爲 BitMap 來支持人羣圈選。例如:性別標籤有男、女、未知三個標籤值,標籤值之間有明顯的區分度,生成 BitMap 之後每個 BitMap 被使用的概率也較高,其比較適合構建標籤 BitMap。但是對於平均在線時長、關注粉絲數等數值型的標籤,標籤值是不可枚舉的或者數量太過龐大,標籤值之間沒有明顯的區分度,此類標籤不適合構建 BitMap。生成 BitMap 會消耗大量的計算和存儲資源,如果標籤值區分度較小,生成的 BitMap 數據被使用到的概率較低,是對計算和存儲資源的浪費。這個時候我們就不會創建 bitmap,而還是寬表。

使用畫像寬表還是 BitMap 要根據業務特點來決定。基於寬表中全量用戶的明細數據可以實現所有的人羣圈選功能,但是採用 BitMap 方案的人羣創建速度相比寬表模式可以提升 50% 以上。BitMap 適用的標籤類型和業務場景有限,要結合實際的數據進行判斷。業界大廠一般使用混合模式,優先通過 BitMap 進行人羣創建,不適用的場景下兜底使用畫像寬表進行人羣圈選。採用混合模式要考慮對齊畫像寬表和 BitMap 的標籤時間,這增加了工程的實現複雜度。

在實際場景中,可很時候都會會面臨混合數據圈存的情況,既有寬表的明細數據,又有 BitMap 數據。所以在這種情況下,我們的關注點還是主要在於規則圈選引擎的優化。如果 我們創建的 BitMap 能夠覆蓋大部分標籤且計算速度比較理想,可以優先考慮使用 BitMap,也就是先先計算 BitMap 的部分,計算完成以後在與明細一起計算,最終自底向上彙總爲執行結果。

總結,客羣的圈選的方法是基於 clickhouse 實現,在某些特定的場景裏引入 BitMap,採取混合圈客的方法,並在後期複雜模式下對圈選規則引擎進行多方便的優化,來全面提高客羣篩選的效率。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/CpJfQIk2brx6q9Ytf6MPTQ