本文來自Spool的開發(fā)者博客,描述了Spool利用Redis的bitmaps相關(guān)的操作,進行網(wǎng)站活躍用戶統(tǒng)計工作。
Redis支持對String類型的value進行基于二進制位的置位操作。通過將一個用戶的id對應(yīng)value上的一位,通過對活躍用戶對應(yīng)的位進行置位,就能夠用一個value記錄所有活躍用戶的信息。如下圖所未,下圖中的bitmap有9個位被置為1,表示這9個位上對應(yīng)的用戶是今天的活躍用戶。其中第15位表示uid為15的用戶,第一位表示uid為0的用戶。(如果你的uid不是從1開始的,比如從100000開始,實際上你也可以相應(yīng)的用uid減去初始值來表示其位數(shù),比如1000000用戶對應(yīng)到bitmap的第一位)
具體的代碼類似下面這樣:
redis.setbit(play:yyyy-mm-dd, user_id, 1)
這樣一次記錄的復(fù)雜度是O(1),在Redis中速度非???。
而我們通過每天換用一個不同的key來將每天的活躍用戶狀態(tài)記錄分開存。并且可以通過一些與或運算計算出N天活躍用戶,和連接N天活躍用戶這樣的統(tǒng)計數(shù)據(jù)。
如下圖,第一行表示星期一的活躍用戶情況,第二行表示周二的,以此類推。為樣我們通過對N天的活躍用戶記錄取并集操作,就能得出在N天內(nèi)活躍過的用戶列表。
下面表格表示對應(yīng)一天,一周,一個月統(tǒng)計時所花費的時間。
Period | Time (ms) |
---|---|
Daily | 50.2 |
Weekly | 392.0 |
Monthly | 1624.8 |
下面是具體的java代碼片斷:
1.算出一天的活躍用戶數(shù)量
import redis.clients.jedis.Jedis;import java.util.BitSet;... Jedis redis = new Jedis("localhost");... public int uniqueCount(String action, String date) { String key = action + ":" + date; BitSet users = BitSet.valueOf(redis.get(key.getBytes())); return users.cardinality(); }
2.計算某幾個內(nèi)活躍用戶的數(shù)量(某一天活躍就算,所以是取并集)
import redis.clients.jedis.Jedis;import java.util.BitSet;... Jedis redis = new Jedis("localhost");... public int uniqueCount(String action, String... dates) { BitSet all = new BitSet(); for (String date : dates) { String key = action + ":" + date; BitSet users = BitSet.valueOf(redis.get(key.getBytes())); all.or(users); } return all.cardinality(); }
具體的用法還很多,比如你還可以對獨特終端的用戶單獨記一個bitmap,這樣就可以統(tǒng)計不同終端用戶的活躍情況。有的同學(xué)會說用set也能實現(xiàn)同樣的效果。但使用set在內(nèi)存使用量上是會大很多的。
如果你還有其它Redis好玩的應(yīng)用場景。也歡迎通過NoSQLFan分享給更多朋友。