Fourinone-4.17.10 新版本發佈:單機毫秒完成上億大數據常規統計

雖然現在最火的是AI,但是大數據和計算能力仍然是機器學習/AI算法的重要支撐,我們的業務場景大部分是通過手機終端、服務器日誌不斷產生日誌數據,通過消息通道發送到大數據平臺進行存儲、加工和統計,然後在統計數據之上提供算法挖掘用戶偏好行爲和畫像,爲此,我們的關鍵任務是需要從海量數據裏統計分析每項產品的去重用戶、新增用戶、pv、uv、dau(日活)、mau(月活)等指標,這個過程存儲佔用越少,計算時間越快越好。Fourinone(CoolHash)擁有原創數據庫引擎設計能力和知識產權,能夠在引擎層面靈活擴充各種功能支持,爲了提供大數據統計計算的最優解決方案,4.17在引擎上增強了以下特性:

 

一、增加了自加和存在新增兩個原子操作

1、Object putPlus(String key, T plusValue)

如果key對應的value是數字類型(int、long、double、float),自增加plusValue(數字類型),如plusValue=1,表示每次自增1,plusValue也可以是小數。如果key對應的value是字符串類型,自增加plusValue(字符串),會累加到原字符串後面,可以用分隔符隔開。putPlus的返回值爲該key的上一個值。

 

2、Object putNx(String key, T value)

如果key存在,則不操作,如不存在寫入value。putNx返回值爲key操作前值,爲null表示不存在,否則返回已有值。

 

利用putPlus和putNx可以完成很多原子操作,如count類計數統計,在開源包指南附帶的CountDemo.java裏的countTest方法演示了putPlus的使用,在ThreadClient.java的putPlusTest方法和putNxTest方法演示了多線程下的使用。

pvTest方法演示了計算pv,如果id不存在則寫入,並將pv數自加1,其他線程發現id存在,則無法更新pv數

Object nx = chc.putNx("v0_"+i, i);

if(nx==null)

chc.putPlus("pv_v0",1);

 

二、增加了客戶端本地和存儲引擎端強大的bitmap支持

上面通過putPlus和putNx原子操作可以計算pv,但並不是最高效的方案,使用bitmap有兩個非常顯著的優勢:位存儲佔用空間低,位計算效率高。將需要做統計計算的id轉換成數字序號,每個只佔1個bit,對於20億的用戶id,只需要20億bit約238m大小,壓縮後佔用空間更小,最少只要200k;通過單個bitmap可以完成去重操作,通過多個bitmap的且、或、異或、反等位操作可以完成日活、月活、小時分鐘活躍、重度用戶、新增用戶、用戶流向等絕大部分的統計計算,而且能在單機毫秒級完成,真正做到實時計算出結果,同比hadoop/hive離線計算執行“select distinct count…from…groupby join…”類似sql的方式統計,往往需要幾百臺機器,耗用30分鐘才能完成,對比非常懸殊,而且容易形成大量sql任務調度和大表join給集羣帶來繁重壓力。(圖)

 

 

 

 

 

1、去重用戶:求1的總數

2、活躍用戶:取或

bitmap1 | bitmap2

3、非活躍用戶:取反:

~bitmap1

4、重度用戶:取且:

Bitmap1 & bitmap2

5、新增用戶:取或加異或:

(Bitmap1 | bitmap2)^bitmap1

6、多種指標組合:

Bitmap1 & bitmap2 & bitmap3 &…

等等

 

同時提供bitmap本地和引擎端互通實現,能夠進行更靈活的架構設計,可以將bitmap壓縮存儲到任何數據庫上,客戶端拉回後完成聚合計算,計算完成的結果再寫回數據庫。也可以多個客戶端同時連接到CoolHash存儲引擎上,通過引擎的bitmap操作支持完成去重、聚合、解壓縮等支持。BitMap結合存儲引擎如下圖:



 

 

1、本地內存實現,CoolBitSet實現了以下bitmap功能:

CoolBitSet(int maxSize),可指定大小限制,默認1000萬大小,本地沒有最大限制,可以使用多個分區的bitmap表示整型範圍或長整型範圍的數據,每個1000萬的bitmap壓縮後在2m以內,很適合放入kv存儲。

(1)基本操作:CoolBitSet提供基本的get(int n)、set(int n)、put(int n)操作,其中put爲存在返回get,不存在set,除外還提供批量操作:int set(CoolBitSet cbs): 將另外一個bitmap對象合併到當前bitmap,並返回新增的數量。

(2)聚合操作:求且、求或、異或、求反、求新增

CoolBitSet and(CoolBitSet cbs):兩個CoolBitSet求且,更新到當前對象,並返回該對象引用

CoolBitSet or(CoolBitSet cbs):兩個CoolBitSet求或,同上

CoolBitSet xor(CoolBitSet cbs):兩個CoolBitSet求異或,同上

CoolBitSet andnot():將該CoolBitSet對象求反,同上

CoolBitSet setNew(CoolBitSet cbs):求當前CoolBitSet的新增用戶,並返回新增用戶結果的對象引用

(3)求總數:int getTotal()返回該CoolBitSet的用戶總數,bit位是1的總數量

(4)求容量:int getSize()返回該CoolBitSet的容量大小

(5)調試查看:String toString(int num)返回該CoolBitSet的二進制字符串,爲了減少長度,參數num爲需要查看的byte數,如num=5表示查看前5個byte的二進制串

 

和java的bitmap的實現區別:jdk自帶的BitSet類是以long數組實現,而且只能初始化大小,無法限制大小,每個bitset要耗用幾百m的內存,多個bitmap容易造成空間大量浪費,BitSet類只是本地內存實現,沒有分佈式存儲引擎持久化支持。

 

2、引擎端持久化實現,CoolHashClient提供了以下接口用來操作存儲引擎:

(1)int putBitSet(String key, int index):

單項操作,類似CoolBitSet的put,第一個參數爲bitmap的key,第二個參數將該bitmap的index位置設爲1。

(2)boolean getBitSet(String key, int index):

單項操作,類似CoolBitSet的get,第一個參數爲bitmap的key,第二個參數需要獲取的index位置的值。

(3)int putBitSet(String key, CoolBitSet cbs):

批量操作,類似CoolBitSet的批量set,將另外一個bitmap對象合併到指定key的bitmap,並返回新增的數量。獲取CoolBitSet對象仍然使用get接口Object get(String key)

(4)Object putBitSet(String key, CoolBitSet cbs, String logical):

聚合操作,參數logical可以設置爲“and”,“or”,“xor”,“andnot”,”new”之一,對於“andnot”,參數cbs並不起作用,可以傳入任意不爲空的CoolBitSet對象。聚合操作會作用到該key指定的bitmap上,返回值爲聚合後的CoolBitSet對象。

 

以上操作遵循CoolHash的k/v存儲約束,k爲字符串,v不超過2m(可修改默認配置大小)。

注意CoolBitSet對象可以用三種方式進行k/v存儲和壓縮:

(1)存儲爲bitSet格式,合併數據:putBitSet(String key, CoolBitSet cbs)

(2)存儲爲bitSet格式,直接覆蓋:put(String key, CoolBitSet cbs)

(3)普通kv存儲格式,非bitSet格式:put(String key, cbs.getBytes());

由於是對象存儲,三種put方式都會對value數據進行壓縮,採用壓縮率和耗時比較平衡的gzip壓縮。

前兩種bitSet格式存儲方式,會驗證CoolBitSet大小不能超過1億,否則不能提交。

第三種普通kv存儲格式,沒有1億的限制,只要壓縮後大小不超過2m,可以正常提交,但由於不是CoolBitSet格式,存儲引擎無法識別做聚合等操作。

 

和redis的bitmap的實現區別:redis實現了bitmap的單項操作和聚合操作,但是沒有批量操作,也沒有壓縮,通過offset指定偏移量的方式分配空間容易造成浪費。

 

開源包指南附帶CountDemo.java裏的演示:

bitSetTest方法:先演示了全量存儲,寫入10億數據到1個bitmap,耗時不到1秒;再演示了分區存儲,將1億大小的數據分成10個1000萬大小的bitmap存儲。

realtimeStatistics方法:演示基於bitmap做用戶去重、活躍用戶、非活躍用戶、重度用戶、新增用戶等實時計算

retainLocal方法和retainServer方法:分別演示瞭如何使用本地內存和存儲引擎計算用戶留存

 

3、增加String類型的bitmap支持:

StringBitMap實現了String類型的bitMap,通過對hash算法的改進,能夠做到1億字符串數據只有200多的碰撞率,5000萬內數據幾乎沒有碰撞率,對於不超過1億的數據是很合適的,但1億以上的字符串數量仍然不合適,碰撞率會大幅上升。開源包指南附帶CountDemo.java裏的stringBitMapTest方法演示了模擬1000萬隨機生成的15位IMEI設備號,並返回碰撞個數。

 

4.17.10版本同時提供jdk1.8.0_151編譯下"fourinone.jar"包和jdk1.7.0_80編譯下"fourinone-jdk7.jar"包。4.17.10版本更新github code和gitee code,本版本所有開源內容已經進行了公司報備,感謝領導對開源的支持。

https://github.com/fourinone/fourinone 

https://gitee.com/fourinone/fourinone 

 

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