基於 ClickHouse 的用戶行爲分析系統

一、用戶行爲分析系統簡介

用戶行爲分析系統利用用戶的行爲明細數據針對用戶的有序行爲做分析,是一個靈活度較高的實時分析系統,具備參數篩選、人羣篩選、多維度拆分、羣組拆分等功能。系統結構如圖 1.1 所示,具有事件分析、漏斗分析、歸因分析、路徑分析、行爲跟蹤五個基礎的分析功能,以行爲明細數據作爲分析的基礎元數據,以羣組數據作爲分析人羣篩選數據。在多種計算引擎的嘗試及性能對比下 [1],最終選用 ClickHouse 作爲計算引擎。

圖 1.1 用戶行爲分析系統結構圖

二、ClickHouse 集羣部署

ClickHouse 的表分爲兩種:分佈式表、本地表。分佈式表是邏輯上的表,可以理解爲數據庫中的視圖,一般查詢都查詢分佈式表。分佈式表引擎會將我們的查詢請求路由本地表進行查詢, 然後進行彙總最終返回給用戶;本地表是實際存儲數據的表。

ClickHouse 集羣結構如圖 2.1 所示,依靠 ReplicatedMergeTree 引擎族與 ZooKeeper 實現了複製表機制,成爲其高可用的基礎。依靠 Distributed 引擎實現了分佈式表機制, 在所有數據分片(shard)上建立視圖進行分佈式查詢。依靠 ZooKeeper 實現每個 shard 中的本地表之間的數據同步。

圖 2.1 ClickHouse 集羣結構圖

三、Hive2ClickHouse 推數

Hive 到 ClickHouse 採用 MapReduce 傳輸數據,推數流程如圖 3.1 所示,從 hive 讀取到的數據根據用戶 id 路由並寫入所有 shard 的其中一個本地表中,最後由 Zookeeper 完成各 shard 本地表之間的數據同步。數據根據用戶 id 路由的目的是爲了實現 shard 之間數據的解耦以提高查詢效率,第五章將詳細描述。

ClickHouse 的分佈式表也具有分發數據的功能,但直接向分佈式表寫入數據存在着以下弊端:

本文選用在 map 中做數據路由後直接向本地表寫入數據的方案。首先查詢 System 庫中的 Clusters 表,獲取集羣的 shard 信息(主要是 shard 中所有設備的 ip),接着將從 hive 表中讀取到的數據按一定規則(用戶 id % 集羣 shard 個數)路由存儲到相應 shard 的 batch 中,當 batch 的數據量達到設定值時即可向對應的 shard 推送數據。從 Clusters 表獲取到的對應 shard 的一組設備,向其中一個設備的本地表推送數據,若失敗則嘗試其他設備。

圖 3.1 Hive2ClickHouse 推數流程圖

四、用戶數據及建表

用戶數據包括行爲明細數據和羣組數據,行爲明細數據即用戶某個時間點發生某個行爲的記錄,羣組數據即圈定好或計算出的人和羣組關係數據。行爲明細數據作爲分析的元數據,羣組數據作爲元數據篩選條件,可實現針對特定人羣的行爲分析。

  1. 行爲明細數據

行爲明細數據記錄了用戶某個時間點發生的某個行爲,由用戶 id、時間戳、行爲屬性組成,爲了便於管理用戶行爲明細數據,如圖 4.1 所示,相同的行爲屬性的行爲被打包成事件,事件由事件名及事件組成,同時相同屬性的事件歸爲相同的事件類型。

圖 4.1 事件用戶行爲關係圖

事件打包後的行爲明細數據由事件類型、事件名、事件屬性組成,那麼行爲明細數據表的字段有:用戶 id、事件類型、事件名、時間戳、事件屬性字段,其中事件屬性字段由事件決定,因此事件屬性字段設爲公用字段,由數據庫管理事件及事件屬性字段之間的關係。

行爲數據的事件打包一方面是爲了便於使用者的理解及選擇,另一方面是爲了數據的快速篩選,即將事件類型作爲行爲明細數據表的一個分區。

行爲分析是在選定的事件中針對用戶有時間順序的行爲串的分析,篩選選定事件期望事件的所有數據存儲在一起,時間順序決定了對時間戳期望讀取到的數據已經按時間排好順序,用戶的行爲串期望同一個用戶的數據在存儲在一起以減少 group by 用戶的時間。因此事件名、時間戳、用戶 id 作爲排序字段,優先級事件名 > 用戶 id > 時間戳。

  1. 羣組數據

羣組數據有兩種類型:標籤羣組數據、事件羣組數據。標籤羣組數據爲人及羣組關係明確的數據,事件羣組數據需要從行爲明細數據中提取人羣數據。羣組數據選用 bitmap 存儲節約存儲量的同時,位圖計算提升計算速率 [2]。

標籤羣組數據來自 DMP 計算好的人與羣組之間的關係,爲了保證羣組數據根據用戶 id 路由,標籤羣組的創建分如圖 4.2 所示爲三步,從 Hive 表推入到 ClickHouse 的數據以用戶 id 爲粒度,接着將用戶 id 爲粒度的數據拆分後寫入拆分表,最後將拆分表中的數據寫入表引擎爲 AggregatingMergeTree[3] 的聚合表中,聚合表以羣組作爲聚合粒度用戶 id 組合成 bitmap。

圖 4.2 標籤羣組創建流程圖

由於事件羣組數據已經跟隨行爲明細數據實現了用戶 id 路由,因此需執行標籤羣組的第三步,從事件中獲取到用戶與羣組之間的關係後將數據寫入到表引擎爲 AggregatingMergeTree 的聚合表中即可獲得事件羣組數據。

五、 行爲分析

基本的用戶行爲分析場景分爲有事件分析、漏斗分析、路徑分析、行爲跟蹤、歸因分析,分析基本結構如圖 5.1 所示,首先根據時間範圍、事件範圍、羣組範圍篩選出待分析的行爲明細數據範圍,再以用戶 id 及維度爲聚合粒度獲得用戶行爲串,最後對行爲串做聚合分析運算。

圖 5.1 分析基本結構

由於數據根據用戶 id 路由,所以同一個用戶的所有數據只可能存儲在同一個 shard 中,不同的 shard 之間的用戶行爲串無耦合,因此分析 sql 可以同時在所有的 shard 執行,並將每個 shard 的分析結果累加,實現並行計算。與分佈式表的並行計算不同的是,分佈式表是並行計算明細層的函數轉換並將轉換結果彙總做聚合,而 shard 並行是並行計算函數轉換及明細聚合並對聚合結果彙總做累加,因此 shard 並行具有更高的計算效率。

  1. 事件分析

事件分析即明細數據的聚合運算,是這些分析場景中特殊的一種分析,由於事件分析並不關心用戶行爲串,只需要對所有用戶的事件明細做統計,因此並不需要用到上述的分析基本結構。事件分析是對所有用戶的明細數據統計,數據根據用戶 id 路由並不能保證 shard 之間的待統計數據無耦合,例如拼單場景下的訂單量的統計(distinct 訂單號),不同 shard 之間存在具有相同的訂單號的情況,因此不能直接將每個 shard 的計算結果累加。

爲了解決此類 distinct 分析的矛盾,增加一個字段用來標記順序,實現將 distinct 字段與人綁定。仍然以拼單爲例,將單號與人綁定如圖 5.2 所示,並行計算的情況下以【distinct 訂單號】統計訂單量結果爲 2,利用順序字段即可更正這種錯誤,即以【distinct (case when 順序 = 1 then 訂單號 else null end)】統計訂單量。實際應用中需要統計 distinct 且不同的人會共享的字段並不多,因此本文選用此方法以空間換時間。

圖 5.2 拼單場景下單號與人綁定

  1. 漏斗分析

漏斗分析即一定窗口轉化期內用戶的轉化率的計算,ClickHouse 提供了計算漏斗的函數 windowFunnel (時間窗口)(時間戳,第一層條件,第二層條件......)[4],官方自帶的漏斗函數計算流程如圖 5.3 所示,首先將傳入的用戶行爲串序列按時間戳排序,如果序列傳入時已經跑排好順序此步驟將減少耗時;接着提取序列的層級信息以獲得層級序列便於特徵向量的提取;然後遍歷層級序列生成特徵向量,特徵向量是在順序遍歷層級序列的情況下記錄每一層級符合時間窗口條件的時間戳;最後統計出特徵向量中不爲空的時間戳個數,作爲該用戶的最大匹配層級。

圖 5.3 ClickHouse 官方 windowFunnel 函數計算流程

ClickHouse 官方 windowFunnel 函數雖然能夠根據用戶行爲串統計出該用戶的最大匹配層級,但並不能實現維度拆分,本文自定義漏斗維度拆分函數 dimFunnel (時間窗口)(時間戳,維度,第一層條件,第二層條件…),能夠實現第一層級的維度的拆分功能,dimFunnel 函數計算流程如圖 5.4 所示,在 windowFunnel 的函數的基礎上用一個 key 爲維度 value 爲特徵向量的 map 來記錄第一層級的各個維度下的特徵向量以實現第一層級的維度拆分,最後遍歷維度 - 特徵向量 map,獲取每個維度下特徵向量不爲空的個數作爲每個維度下用戶的最大匹配層級。

圖 5.4 自定義 dimFunnel 函數計算流程

  1. 路徑分析

路徑分析即一定會話時間內的用戶訪問路徑的統計,計算流程如圖 5.5 所示,首先對行爲串按時間戳排序,若數據按照時間戳順序排好序存儲可減少此步驟的耗時;接着從起始事件往後或者終止事件往前截取有效的行爲串並相鄰爲去重;然後計算前後行爲的時間戳的間隔時間,若間隔時間大於會話時間則截斷序列;接着截取會話訪問時間內序列的有效段,取十條行爲記錄;最後對所有的序列做篩選,保留滿足起始事件、終止事件的序列。

圖 5.5 路徑分析計算流程

  1. 行爲跟蹤

行爲跟蹤又被稱爲留存分析,即產生 A 行爲的用戶在此之後的一定時間內產生 B 行爲的狀況統計,ClickHouse 官方函數 retention(起始條件,後續條件 1,後續條件 2 …)[5] 可以用作留存分析的計算。retention 函數的計算流程如圖 5.6 所示,首先初始化結果 array;接着 array[0] 爲是否存在滿足起始條件的行爲,0 爲不滿足,1 爲滿足;然後 bian 遍歷後續條件,在起始行爲條件滿足的且存在滿足第 i 個後續條件的情況下 Array[i-1]=1。

圖 5.6 ClickHouse 官方 retention 函數計算流程

留存按起始事件的維度劃分計算流程如圖 5.7 所示,由於留存特性是隻有滿足起始條件後續條件纔會生效,因此只需對每日的起始事件用一個維度 Array 記錄維度值,對彙總維度值展開至多行後,若此日的起始事件維度 Array 不包含此緯度值則次日在該維度下的留存清零,即此維度下此日沒有滿足起始條件的行爲。最後以活動爲粒度聚合留存結果。

圖 5.7 留存分維度計算流程

  1. 歸因分析

歸因分析即用戶產生一組行爲後對於促成最終目標的貢獻度的統計 [6],歸因分析常用的幾類有:首次歸因、末次歸因、線性歸因。歸因分析計算流程如圖 5.8 所示,首先將行爲串按照時間戳排序,若數據按照時間戳順序排好序存儲可減少此步驟的耗時;接着以目標事件爲終點截斷行爲串,得到一系列以目標事件爲終點的行爲串;然後遍歷行爲串獲取歸因明細數據,歸因明細由歸因比例、計算指標、維度事件 id、維度值組成;最後對歸因明細數據以維度事件 id 及維度值爲粒度聚合,歸因比例聚合結果除以所有歸因比例聚合結果的和爲歸因比例,計算指標的聚合結果爲目標事件的指標歸因到該事件維度的值。

圖 5.8 歸因分析計算流程

六、總結與展望

本文介紹了用戶行爲分析系統的組成、ClickHouse 集羣的部署、從 Hive 到 ClickHouse 的推數、用戶數據及建表以及五種行爲分析,以 shard 數據解耦爲前提實現並行計算,雖然計算效率得以提升,但計算消耗線程數爲集羣 shard 個數的倍數。官方提供原始函數的同時也開放了自定義函數入口,可通過自定義函數進一步提升計算效率。

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