setbit實現活躍用戶統計

通過《The Little Redis Book》看到有這樣一篇博文《REDIS BITMAPS – FAST, EASY, REALTIME METRICS》,這次就不翻譯了,已經有其他中文的版本。寫這個目的在於一開始沒看懂,想把整個詳細的思想寫出來。

先看setbit的使用。https://redis.io/commands/setbit。


先說位圖,bitmap,根據二進制不同位置的值即可表示其代表的值。這就讓我想起了上過的數字圖像處理了。


setbit(key,offset,value) 
offset是從左往右算的位數,從零開始,即高位往低位的值,例如100011,offset爲0的時候值爲1, 4和5的值都爲1。
本身redis存儲字符串,以二進制格式存儲。
所以文章裏面的配圖是錯的。反過來了。


返回值是0和1,表示的是,設置前該offset位置下bit的值。

The offset argument is required to be greater than or equal to 0, and smaller than 2^32 (this limits bitmaps to 512MB)
bitmap本身offset的限制就是0到2^32,內存限制爲521MB,分配所需時間才幾百ms,剛剛好是2^32個bit,也就是4294967296,也就是說,offset最大能去到4294967296-1去。有四十二億。


照着文章作者的思路。他們網站封頂有1億多的用戶。每個用戶都有自己的用戶ID。一個用戶做操作,我們有個40多億長度全爲0的位圖。
我們只需要在這個根據ID做位置定位(即offset偏移)將其改爲1就算記錄了該用戶的操作了。每個用戶的id不一樣,將日期作爲key,然後根據用戶id的唯一性作爲offset偏移。需要保證用戶id不會超過4294967296-1。


而要統計一天用多少用戶做操作(爲什麼說操作不說登錄了,因爲不僅僅限於登錄,瀏覽某某東西,寫日記等等都可以用這種方法)
,除去重複,總數就是這個位圖裏面值爲1的個數。

將文章的代碼具體實現了一下。

Jedis j = new Jedis("localhost");
//auth password
j.auth("myredis");
 
//2016-12-3  login operation user
j.setbit("login:2016-12-3".getBytes(), 1, true);
j.setbit("login:2016-12-3".getBytes(), 124431, true);
j.setbit("login:2016-12-3".getBytes(),1231, true);
j.setbit("login:2016-12-3".getBytes(), 323121, true);
BitSet b = BitSet.valueOf(j.get("login:2016-12-3".getBytes()));
 
//the number of bit value 1
int lognum3 = b.cardinality();
System.out.println("2016-12-3  login user number: "+lognum3);
 
 
//2016-12-3  login operation user
j.setbit("login:2016-12-4".getBytes(), 1, true);
j.setbit("login:2016-12-4".getBytes(), 1231231, true);
j.setbit("login:2016-12-4".getBytes(), 334441, true);
BitSet b2 = BitSet.valueOf(j.get("login:2016-12-4".getBytes()));
 
int lognum4 = b2.cardinality();
System.out.println("2016-12-4  login user number: "+b2.cardinality());
 
b.or(b2);
//or操作之後 同樣userid的記錄會重合不做記錄,所以具體的數據統計看自己的需求而定
int lognumexceptsameuser = b.cardinality();
int logtotalnum = lognum3+lognum4;
System.out.println("2016-12-3 to 2016-12-4 login user number except same userid: "+lognumexceptsameuser);
System.out.println("2016-12-3 to 2016-12-4 login user number: "+logtotalnum);


輸出:


2016-12-3  login user number: 4
2016-12-4  login user number: 3
2016-12-3 to 2016-12-4 login user number except same userid: 6
2016-12-3 to 2016-12-4 login user number: 7

如果用戶數登錄求和時,不同日期的用戶ID登錄需要計算,那麼就不要做或操作,將每天的登錄數求和即可。
如果求的是一個月內,用戶登錄總數,用戶登錄多次只算一次的話,只需要將這段時間內的bit進行或操作即可。

確實快,不需要你記錄到數據庫。只需要一個bit就能記錄該用戶登錄。

利用了redis本身用內存存儲的優勢。這種需求下的解決方案確實是快,方便。

當然這種只針對這種特殊的需求,你非要每天用戶重複登錄的次數也算進去,當然就不能用這種方法。所以,針對不同的需求,找出最優方案纔是最好的。
--------------------- 

轉載自:https://blog.csdn.net/iaiti/article/details/53446667

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