Clickhouse在大數據分析平臺-留存分析上的應用

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"導語 | 本文實踐了對於千萬級別的用戶,操作總數達萬級別,每日幾十億操作流水的留存分析工具秒級別查詢的數據構建方案。同時,除了留存分析,對於用戶羣分析,事件分析等也可以嘗試用此方案來解決。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文章作者:陳璐,騰訊高級數據分析師"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"  背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能聽說過Growingio、神策等數據分析平臺,本文主要介紹實現留存分析工具相關的內容。留存分析是一種用來分析用戶參與情況/活躍程度的分析模型,可考查進行初始行爲後的用戶中,有多少人會進行後續行爲,這是衡量產品對用戶價值高低的重要指標。如,爲評估產品更新效果或渠道推廣效果,我們常常需要對同期進入產品或同期使用了產品某個功能的用戶的後續行爲表現進行評估 [1]。大部分數據分析平臺主要包括如圖的幾個功能(以神策"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a0/a0f8614eebe5137ad227303d6cbb3ff0.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要介紹留存分析工具的優化方案(只涉及數據存儲和查詢的方案設計,不涉及平臺)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我想每個數據/產品同學在以往的取數分析過程中,都曾有一個痛點,就是每次查詢留存相關的數據時,都要等到天荒地老,慢!而最近採用優化方案的目的也是爲了提高查詢的效率和減少數據的存儲,可以幫助產品快速地查詢/分析留存相關的數據。優化方案的核心是在Clickhouse中使用Roaringbitmap對用戶進行壓縮,將留存率的計算交給高效率的位圖函數,這樣既省空間又可以提高查詢速度。希望本實踐方案可以給你帶來一些幫助和啓示。下面主要分3個部分詳細介紹:Roaringbitmap簡介、思路與實現、總結與思考。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"一、Roaringbitmap 簡介"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面先簡單介紹一下高效的位圖壓縮方法Roaringbitmap。先來看一個問題:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"給定含有40億個不重複的位於[0,2^32-1]區間內的整數集合,如何快速判定某個數是否在該集合內?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"顯然,如果我們將這40億個數原樣存儲下來,需要耗費高達14.9GB的內存,這是難以接受的。所以我們可以用位圖(bitmap)來存儲,即第0個比特表示數字0,第1個比特表示數字1,以此類推。如果某個數位於原集合內,就將它對應的位圖內的比特置爲1,否則保持爲0,這樣就能很方便地查詢得出結果了,僅僅需要佔用512MB的內存,不到原來的3.4% [3]。但是這種方式也有缺點:比如我需要將1~5000w這5000w個連續的整數存儲起來,用普通的bitmap同樣需要消耗512M的存儲,顯然,對於這種情況其實有很大的優化空間。2016年由S. Chambi、D. Lemire、O. Kaser等人在論文《Better bitmap performance with Roaring bitmaps》與《Consistently faster and smaller compressed bitmaps with Roaring》中提出了roaringbitmap,主要特點就是可以極大程度地節約存儲及提供了快速的位圖計算,因此考慮用它來做優化。對於前文提及的存儲連續的5000w個整數,只需要幾十KB。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它的主要思路是:將32位無符號整數按照高16位分桶,即最多可能有2^16=65536個桶,論文內稱爲container。存儲數據時,按照數據的高16位找到container(找不到就會新建一個),再將低16位放入container中。也就是說,一個roaringbitmap就是很多container的集合 [3],具體細節可以自行查看文末的參考文章。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"二、思路與實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們的原始數據主要分爲:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.用戶操作行爲數據table_oper_raw"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包括時間分區(ds)、用戶標識id(user_id)和用戶操作行爲名稱(oper_name),如:20200701|6053002|點擊首頁banner 表示用戶6053002在20200701這天點擊了首頁banner(同一天中同一個用戶多次操作了同一個行爲只保留一條)。實踐過程中,此表每日記錄數達幾十億行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2.用戶屬性數據table_attribute_raw"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"表示用戶在產品/畫像中的屬性,包括時間分區(ds)、用戶標識(user_id)及各種用戶屬性字段(可能是用戶的新進渠道、所在省份等),如20200701|6053002|小米商店|廣東省。實踐過程中,此表每日有千萬級的用戶數,測試屬性在20+個。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們需要根據這兩類數據,求出某天操作了某個行爲的用戶在後續的某一天操作了另一個行爲的留存率,比如,在20200701這天操作了“點擊banner”的用戶有100個,這部分用戶在20200702這天操作了“點擊app簽到”的有20個,那麼對於分析時間是20200701,且“點擊banner”的用戶在次日“點擊app簽到”的留存率是20%。同時,還需要考慮利用用戶屬性對留存比例進行區分,例如只考慮廣東省的用戶的留存率,或者只考慮小米商店用戶的留存率,或者在廣東的小米商店的用戶的留存率等等。一般來說,求留存率的做法就是兩天的用戶求交集,例如前文說到的情況,就是先獲取出20200701的所有操作了“點擊banner”的用戶標識id集合假設爲S1,然後獲取20200702的所有操作了“點擊app簽到”的用戶標識id集合假設爲S2,最後求解S1和S2的交集:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/02/02743b7ea3833b16910bbc023887fe68.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,當s1和s2的集合中用戶數都比較大的時候,join的速度會比較慢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在此我們考慮前文說到的bitmap,假若每一個用戶都可以表示成一個32位的無符號整型,用bitmap的形式去存儲,S1和S2的求交過程就是直接的一個位比較過程,這樣速度會得到巨大的提升。而Roaringbitmap對數據進行了壓縮,其求交的速度在絕大部分情況下比bitmap還要快,因此這裏我們考慮使用Roaringbitmap的方法來對計算留存的過程進行優化。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.數據構建"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整個過程主要是:首先對初始的兩張表——用戶操作數據表table_oper_raw和用戶篩選維度數據表table_attribute_raw中的user_id字段進行編碼,將每個用戶映射成唯一的id(32位的無符號整型),分別得到兩個新表table_oper_middle和table_attribute_middle。再將他們導入clickhouse,使用roaringbitmap的方法對用戶進行壓縮存儲,最後得到壓縮後的兩張表table_oper_bit和table_attribute_bit,即爲最終的查詢表。流程圖如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/cf/cfec48d338bfe22ac3f641afad82259b.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1).生成用戶id映射表"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,需要構建一個映射表table_user_map,包含時間分區(ds)、用戶標識id(user_d)及映射後的id(id),它將每個用戶(String類型)映射成一個32位的無符號整型。這裏我們從1開始編碼,這樣每個用戶的標識就轉化成了指定的一個數字。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/81a11cc32e6cbda85d9e5fac77b80efc.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2).初始數據轉化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分別將用戶操作數據表和用戶篩選維度數據中的imei字段替換成對應的數值,生成編碼後的用戶操作數據:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/eb/ebc6df8f76a3be582241e62ae383d4a4.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和用戶篩選維度數據:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/80/805f07001732dfc0bd4a39f612125287.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3).導入clickhouse"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先在clickhouse中創建相同結構的表,如table_oper_middle_ch:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/26/26cbd524bf03915acac345dd550a9968.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣的,在clickhouse中創建表table"},{"type":"text","marks":[{"type":"italic"}],"text":"attribute"},{"type":"text","text":"middle_ch。然後用spark將這兩份數據分別導入這兩張表。這一步導入很快,幾十億的數據大概10分多鐘就可以完成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4).Roaringbitmap壓縮"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於用戶操作流水數據,我們先建一個可以存放bitmap的表table_oper_bit,建表語句如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a7/a79e15c1feab1050c8e03ae2d903386a.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶屬性數據table_attribute_bit也類似:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/33e79e98b9fed2c5646ed042289f7295.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏索引粒度可設置小值,接着用聚合函數groupBitmapState對用戶id進行壓縮:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c8/c878f294a44809772995c75b4a90a59f.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣,對於用戶操作數據表,原本幾十億的數據就壓縮成了幾萬行的數據,每行包括操作名稱和對應的用戶id形成的bitmap:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1b/1b633e6561929ea4919ed3d13a0d0b86.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣的,用戶屬性的數據也可以這樣處理,得到table_attribute_bit表,每行包括某個屬性的某個屬性值對應的用戶的id形成的bitmap:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/06/0642c9acb04b42a4843261342a505ec7.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,數據壓縮的過程就這樣完成了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.查詢過程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,簡要地介紹下方案中常用的bitmap函數(詳細見文末的參考資料):1.bitmapCardinality"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回一個UInt64類型的數值,表示bitmap對象的基數。用來計算不同條件下的用戶數,可以粗略理解爲count(distinct)2.bitmapAnd"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲兩個bitmap對象進行與操作,返回一個新的bitmap對象。可以理解爲用來滿足兩個條件之間的and,但是參數只能是兩個bitmap3.bitmapOr"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲兩個bitmap對象進行或操作,返回一個新的bitmap對象。可以理解爲用來滿足兩個條件之間的or,但是參數也同樣只能是兩個bitmap。如果是多個的情況,可以嘗試使用groupBitmapMergeState舉例來說,假設20200701這天只有[1,2,3,5,8]這5個用戶點擊了banner,則有:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"# 返回5\nselect bitmapCardinality(user_bit)\nfrom tddb.table_oper_bit\nwhere ds = 20200701 AND oper_name = '點擊banner'"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"又如果20200701從小米商店新進的用戶是[1,3,8,111,2000,100000],則有:"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"# 返回3,因爲兩者的重合用戶只有1,3,8這3個用戶\nselect bitmapCardinality(bitmapAnd(\n\t(SELECT user_bit\n FROM tddb.table_oper_bit\n WHERE (ds = 20200701) AND (oper_name = '點擊banner')), \n\t(SELECT user_bit\n\t FROM tddb.table_attribute_bit\n\t WHERE ds = 20200701 and (attr_id = 'first_channel') and (attr_value IN ('小米商店')))))"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了以上的數據生成過程和bitmap函數,我們就可以根據不同的條件使用不同的位圖函數來快速查詢,具體來說,主要是以下幾種情況:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"a. 操作了某個行爲的用戶在後續某一天操作了另一個行爲的留存:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如“20200701點擊了banner的用戶在次日點擊app簽到的留存人數”,就可以用以下的sql快速求解:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5d/5ddc0c34260a4a25d566a50cc8b18cd3.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"b. 操作了某個行爲並且帶有某個屬性的用戶在後續的某一天操作了另一個行爲的留存:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如“20200701點擊了banner且來自廣東/江西/河南的用戶在次日點擊app簽到的留存人數”:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/39/3985ce67a8cd3bf8eef6ce8c91ca36b1.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"c. 操作了某個行爲並且帶有某幾個屬性的用戶在後續的某一天操作了另一個行爲的留存:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如“20200701點擊了banner、來自廣東且新進渠道是小米商店的用戶在次日點擊app簽到的留存人數”:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/76/76b5ff69caf891a52ae0e78e0206d873.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.實踐效果"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據這套方案做了實踐,對每日按時間分區、用戶、操作名稱去重後包括幾十億的操作記錄,其中包含千萬級別的用戶數,萬級別的操作數。最後實現了:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存儲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原本每日幾十G的操作流水數據經壓縮後得到的表table_oper_bit爲4GB左右/天。而用戶屬性表table_attribute_bit爲500MB左右/天"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查詢速度"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"clickhouse集羣現狀:12核125G內存機器10臺。clickhouse版本:20.4.7.67。查詢的表都存放在其中一臺機器上。測試了查詢在20200701操作了行爲oper_name_1(用戶數量級爲3000+w)的用戶在後續7天內每天操作了另一個行爲oper_name_2(用戶數量級爲2700+w)的留存數據(用戶重合度在1000w以上),耗時0.2秒左右"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/42/424c6d7de6721d55275b411e80a7f79b.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反饋"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後和前端打通,效果也是有了明顯的優化,麻麻再也不用擔心我會轉暈~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/df/dfb4b120457d1c7b98e8685285d0167e.webp","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"三、總結與思考"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總的來說,本方案的優點是:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存儲小,極大地節約了存儲"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查詢快,利用bitmapCardinality、bitmapAnd、bitmapOr等位圖函數快速計算用戶數和滿足一些條件的查詢,將緩慢的join操作轉化成位圖間的計算"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"適用於靈活天數的留存查詢"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"便於更新,用戶操作數據和用戶屬性數據分開存儲,便於後續屬性的增加和數據回滾"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,根據本方案的特點,除了留存分析工具,對於用戶羣分析,事件分析等工具也可以嘗試用此方案來解決。PS : 作者初入坑ch,對於以上內容,有不正確/不嚴謹之處請輕拍~ 歡迎交流~"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"參考文獻:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1] 解析常見的數據分析模型——留存分析:https://www.sensorsdata.cn/blog/jie-xi-chang-jian-de-shu-ju-fen-xi-mo-xing-liu-cun-fen-xi/"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[2] RoaringBitmap數據結構及原理:https://blog.csdn.net/yizishou/article/details/78342499"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[3] 高效壓縮位圖RoaringBitmap的原理與應用:https://www.jianshu.com/p/818ac4e90daf"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[4] 論文:Better bitmap performance with Roaring bitmaps:https://arxiv.org/abs/1402.6407v9?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+DanielLemiresArticlesOnArxiv+(Daniel+Lemire%27s+articles+on+arXiv)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[5] Clickhouse文檔-位圖函數:"},{"type":"link","attrs":{"href":"https://clickhouse.tech/docs/zh/sql-reference/functions/bitmap-functions/","title":null},"content":[{"type":"text","text":"https://clickhouse.tech/docs/zh/sql-reference/functions/bitmap-functions/"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8f/8fb7fabb32cd26a9d673190d25a2be1e.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關注“騰訊雲大數據”公衆號,技術交流、最新活動、服務專享一站Get~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章