秒級去重:ClickHouse在騰訊海量遊戲營銷活動分析中的應用

導語 | 騰訊內部每日都需要對海量的遊戲營銷活動數據做效果分析,而活動參與人數的去重一直是一項難點。本文將爲大家介紹騰訊遊戲營銷活動分析系統——奕星,在去重服務上的技術思路和迭代方案,希望與大家一同交流探討。文章作者:王方曉,騰訊運營開發工程師。

 

一、背景

 

奕星 (EAS) 是騰訊內部專注於遊戲營銷活動分析的系統,在營銷活動效果分析中,奕星遇到一個最大的問題就是對活動參與人數的去重,並給出對應的活動號碼包。單個營銷活動的週期是固定的,但活動與活動之間時間很少會有完全相同的情況。

 

比如A活動時間是1-10號,B活動是5-15號,那麼如果想分別得到 A 和 B 的去重參與人數,則必須分別開啓任務對 A 和 B 在他們所屬的時間區間內進行計算去重。在海量日誌中每天對數千個營銷活動進行類似計算,對資源消耗是一個不小的挑戰。

 

而實際情況下,需要計算的任務量還遠遠不止於此,奕星同時還提供遊戲官網非活動鏈接的去重數據,甚至每個鏈接在每個推廣渠道的去重數據,這個任務量級目前高達每天 50W+ 以上。

 

總結來看,面臨的主要問題就是如何在海量數據的情況下,處理數量巨大的而且週期各不相同的去重計算任務。

 

二、原有解決方案

 

對於這個問題,奕星之前嘗試了不少方案,這裏簡單介紹一下。

 

1. 基於TDW臨時表的方案

 

TDW 是騰訊內部通用的一站式大數據平臺,服務穩定,功能強大。對於這些任務的計算,奕星最早是想借助於 TDW 的計算能力來完成。

 

思路很簡單,就是在 pysql 中循環對每個活動執行對應的 hiveSQL 來完成 T+1 時效的計算。

 

但這個方案最大的缺點就是:任務基本都是順序執行,重複掃描大量日誌,導致效率非常低下,而且從HDFS中拉取最終的去重文件也非常耗時。

 

雖然後面採用多種思路優化,比如將原始日誌先每天統一批量去重一次入到臨時表裏,所有計算基於臨時表來完成等,但最終還是效率無法進一步提高而放棄。

 

2. 基於實時計算+文件增量去重的方案

 

在奕星的數據統計中,基於 Storm 的實時計算任務,主要是提供各個活動的實時 PV 和參與次數等計數類數據。

 

由於內存資源有限,業界也有基於近似去重算法(如 hyperloglog )直接在 Storm 中算出近似去重結果的,但無法給出精確的結果和最終的號碼包文件,所以不符合選型要求。

 

而內存資源有限,更不可能容納下這麼大量的號碼包數據,所以通過內存完全得出最終精確去重結果的方案基本不現實。

 

但內存雖然不能容納整個活動期間的號碼數據或者一天之內的號碼數據,但是否可以容納 1 分鐘,5 分鐘的號碼數據?

 

通過測試計算發現,在內存中緩存 5 分鐘內的去重號碼數據是完全可行的,並且最高可以將原始日誌降低 90% 以上的量級。緩存 1 分鐘的話,最高也可以將原始日誌降低 70% 以上的量級。

 

主要的原因是玩家參與活動的時候是即時參與行爲,比如一個玩家來到一個活動頁面後,一般是連續將活動中能參與的功能都參與下,不會參與完一個等很久再參與下一個,所以導致同一個玩家的日誌時間連續性較高,單位時間窗口內去重後量級會降低很多。

 

基於此,目前奕星主要是基於 Storm 在單位時間窗口內進行初次去重,以達到降低原始數據量級的目的。

 

 

最初的基於 TDW 的去重方案,除了重複掃描等問題外,還有一個問題就是:同一個活動不同日期之間的計算無法前後銜接,比如 A 活動在活動期間(1-10號),每天的計算邏輯基本一致,都是要全量掃描 1-10 號之間的日誌(或中間結果)來完成計算。

 

所以團隊將目光投向如何在活動前期去重的基礎上來增量去重的問題上來。最終選定的方案是基於文件的計算方案,如下圖所示,活動每天都滾動生成最新的去重號碼包文件,而次日同一個活動的日號碼包再與這個總包交叉後得到更新的號碼包文件,如此重複,直到活動結束得到最終的活動號碼包文件。

 

 

3. 基於實時計算+LevelDB增量去重方案

 

文件增量去重的方案,運行了一段時間後,就出現了一個很大的問題:就是每日新增的文件量巨大,日均幾十萬。

 

雖然沒有達到把單臺機器 inode 佔滿的情況,但在增量去重時,大量的小文件 IO 操作,導致增量去重效率非常低,最後被迫只支持高優先級業務的活動和單個活動參與量大於一定閥值的大活動。

 

經過團隊小夥伴的調研,最終將目光鎖定在 Google 的 LevelDB 上,LevelDB 是 Google 開源的持久化 KV 單機數據庫,具有很高的隨機寫,順序讀/寫性能,但是隨機讀的性能很一般。

 

也就是說,LevelDB 很適合應用在查詢較少,而寫入很多的場景,這正好符合我們號碼包去重服務的應用場景。

 

另外號碼包的存儲本身也是一個K-V的存儲,文件名爲 key,文件內容爲 value,正好跟 LevelDB 支持的 K-V 結構類似。

 

 

使用 LevelDB 後,可以毫秒級得到某個活動的準確去重人數,並且可以在 10 秒內導出千萬量級的號碼包文件,相比傳統的文件操作,大大提高了號碼包去重服務的整體效率。

 

三、基於CLickHouse的解決方案

 

 

雖然基於 LevelDB 的去重服務可以很好的滿足大部分營銷活動的人數去重需求。但擴展性較差,數據回溯困難等問題比較突出,類似於基於預計算模式的 OLAP 系統。比如系統只支持活動整個期間內的去重人數計算,如果想知道活動期間內某一段時間內的去重就無法實現。

 

另外如果某個活動引入了髒數據後,只能將整個活動的 K-V 結構刪除後重跑,非常耗時。團隊經過調研後,將目光鎖定到基於 MPP 的 OLAP 方案上。 

 

基於 MPP 的 OLAP 系統,在數據庫非共享集羣中,每個節點都有獨立的磁盤存儲系統和內存系統,業務數據根據數據庫模型和應用特點劃分到各個節點上,每臺數據節點通過專用網絡或者商業通用網絡互相連接,彼此協同計算,作爲整體提供數據庫服務。

 

相對於基於預計算模式的 OLAP 系統來說,它最大的優點就是靈活,擴展性強,而最大的缺點是響應時間不及時,甚至需要較長的時間等待。

 

而在營銷活動效果分析中,往往靈活性比效率要更加重要,或者說效率是可以稍微妥協的一面,所以我們選擇基於 MPP 的 OLAP 系統。

 

目前市面上有很多優秀的 OLAP 系統,但要麼是收費的(Vertica),要麼是基於 hadoop 生態的(presto,Impala),整體架構比較重。

 

而作爲戰鬥民族開源神器的 ClickHouse 不但擁有自己的文件系統和極高的壓縮比,在集羣部署上甚至可以不用 zk 來獨立部署,甚至在性能上“吊打”商業的 OLAP 系統(詳見官方測評數據:https://clickhouse.tech/benchmark/dbms/)。

 

綜合以上考慮,最終選擇了 ClickHouse,去重服務就變成了 SQL 查詢,例如下面這條 SQL 就是查詢 LOL 官網某個頁面在 9 月 6 日這 1 天的 UV:

select uniqExact(uvid) from tbUv where date='2020-09-06' and 

url='http://lol.qq.com/main.shtml';

 

 

在 24 核 96G 內存的機器上,實際測試下來在 1 億條記錄中,精確去重一個參與量爲100W 的活動,僅需 0.1 s 不到,而導出這個號碼包文件只需要 0.2 s 不到。

 

雖然查詢效率上比 LevelDB 有一點差距,但靈活性卻大大提高,可以任意指定時間區間和條件來做去重查詢,符合當前業務場景關注靈活度的需求場景,而且性能上從毫秒到秒級的延遲基本也可以接受。

 

四、結語

 

去重服務的的問題伴隨奕星系統整個開發和運營週期,期間經歷過很多嘗試,部分臨時的嘗試方案尚未在本文列出,但一直的出發點就是業務需求本身,並且結合當時的運維環境來選取對應的技術方案。

 

不追求絕對的高性能(意味成本也高),而關注最合適,易於擴容,搬遷,故障替換等有利於服務長期穩定運營的特性。當然,隨着更多利器的出現,也會去不斷的嘗試使用,畢竟科學技術纔是第一生產力。

 

目前 ClickHouse 在奕星等多個數據系統和諸多個性化營銷分析的場景中落地使用,數據總規模超過 5 千億,還在不斷增長中。

 

本文只是通過對去重問題的歷史回顧順帶簡單介紹了一下 ClickHouse,更多關於ClickHouse 的詳細介紹和實操,可以自行搜索官方文檔和其他分享內容,最後歡迎大家來評論區探討 ClickHouse 有關的更詳細的問題和應用場景。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章