1. 程式人生 > >利用Redis BitMap 統計使用者活躍指標

利用Redis BitMap 統計使用者活躍指標

  bitMap原理 :

    如下: index 從 0 到 9 ,依次對應到一個bit位上,如果index 代表使用者id,bit位上的0 1分表 代表使用者是否登入;
  1. 1     0     1     1     0     1     1     0     1     1
  2. 0     1     2     3     4     5     6     7     8     9
複製程式碼

   redis資料結構中 string 型別,包含了對bitmap的實現;在redis-cli中,可以通過setbit getbit 來對bit進行操作;本文通過jedis來對redis進行操作; 


BitSet工具類:實現對通過jedis.get(key)取出的byte[]值與BitSet的轉換


  1. public class BitSetUtils {
  2.     /**
  3.      * 將BitSet物件轉化為ByteArray
  4.      * @param bitSet
  5.      * @return
  6.      */
  7.     public static byte[] bitSet2ByteArray(BitSet bitSet) {
  8.         byte[] bytes = new byte[bitSet.size() / 8];
  9.         for (int i = 0; i < bitSet.size(); i++) {
  10.             int index = i / 8;
  11.             int offset = 7 - i % 8;
  12.             bytes[index] |= (bitSet.get(i) ? 1 : 0) << offset;
  13.         }
  14.         return bytes;
  15.     }
  16.     /**
  17.      * 
  18.      * @param bytes
  19.      * @return
  20.      */
  21.     public static BitSet byteArray2BitSet(byte[] bytes) {
  22.         BitSet bitSet = new BitSet(bytes.length * 8);
  23.         int index = 0;
  24.         for (int i = 0; i < bytes.length; i++) {
  25.             for (int j = 7; j >= 0; j--) {
  26.                 bitSet.set(index++, (bytes[i] & (1 << j)) >> j == 1 ? true
  27.                         : false);
  28.             }
  29.         }
  30.         return bitSet;
  31.     }
  32. }
複製程式碼


    具體對java中BitSet操作,見http://my.oschina.net/cloudcoder/blog/294810 ,該篇對bitSet用法介紹很詳細;

redis工具類:


  1. public class RedisUtil {
  2.     static {
  3.         initPool();
  4.     }
  5.     private static volatile JedisPool jedisPool;
  6.     private static ResourceBundle resourceBundle;
  7.     public static Jedis getResource() {
  8.         return jedisPool.getResource();
  9.     }
  10.     public static void returnResource(Jedis jedis) {
  11.         jedisPool.returnResource(jedis);
  12.     }
  13.     public static void initPool() {
  14.             if(jedisPool != null){
  15.                 return;
  16.             }
  17.             loadProperties();
  18.             String host = resourceBundle.getString("redis.host");
  19.             String passwd= resourceBundle.getString("redis.passwd");
  20.             int port = Integer.parseInt(resourceBundle.getString("redis.port"));
  21.             JedisPoolConfig config = config();
  22.             jedisPool = new JedisPool(config,host,port,60,passwd);
  23.     }
  24.     private static void loadProperties() {
  25.         resourceBundle = ResourceBundle.getBundle("config/redis-config");
  26.     }
  27.     private static JedisPoolConfig config() {
  28.         JedisPoolConfig config = new JedisPoolConfig();
  29.         return config;
  30.     }
  31.     public static void main(String[]args){
  32.        Jedis jedis= RedisUtil.getResource();
  33.        RedisUtil.returnResource(jedis);
  34.     }
  35. }
複製程式碼


1 統計系統中某天使用者登入的情況:以當天日期做為key ,比如 ‘20150410’ ,對應的 bitMap 的 index 用userId來標示,UserId這裡用  long 型表示,如果id不是以0開頭,可以加上相應的偏移量就OK了;如果該天使用者登入,呼叫activeUser方法,來更改bitMap相應index上的標示;


  1. public void activeUser(long userId, String dateKey) {
  2.     Jedis jedis= RedisUtil.getResource();
  3.     try{
  4.         jedis.setbit(dateKey,userId,true);
  5.     }finally {
  6.         RedisUtil.returnResource(jedis);
  7.     }
  8. }
複製程式碼

如果我們想統計該天使用者登入的數量,及登入的使用者id,可以通過如下方法實現:


//該天使用者總數
  1. public long totalCount(String dateKey) {
  2.     Jedis jedis= RedisUtil.getResource();
  3.     try{
  4.         return jedis.bitcount(dateKey);
  5.     }finally {
  6.         RedisUtil.returnResource(jedis);
  7.     }
  8. }
複製程式碼


//該天登入所有的使用者id
  1. public List<Long> activeUserIds(String  dateKey) {
  2.     Jedis jedis= RedisUtil.getResource();
  3.     try{
  4.         if(jedis.get(key)==null){
  5.                  return null;
  6.          }
  7.         BitSet set= BitSetUtils.byteArray2BitSet(jedis.get(key).getBytes());
  8.         List<Long>list=new ArrayList<Long>();
  9.         for (long i=0;i<set.size();i++){
  10.             if(set.get(i)){
  11.                 list.add(i);
  12.             }
  13.         }
  14.         return list;
  15.     }finally {
  16.         RedisUtil.returnResource(jedis);
  17.     }
  18. }
複製程式碼


   2 如果我們想統計n天,連續登入的使用者數,及UserId:


  1. public List<Long> continueActiveUserCount(String... dateKeys) {
  2.     Jedis jedis= RedisUtil.getResource();
  3.     try{
  4.         BitSet all = null;
  5.         for (String key:dateKeys){
  6.              if(jedis.get(key)==null){
  7.                  continue;
  8.              }
  9.              BitSet set= BitSetUtils.byteArray2BitSet(jedis.get(key).getBytes());
  10.              if(all==null){
  11.                  all=set;
  12.              }
  13.              System.out.println(set.size());
  14.              all.and(set);
  15.         }
  16.         List<Long>list=new ArrayList<Long>();
  17.         for (long i=0;i<all.size();i++){
  18.             if(all.get(i)){
  19.                 list.add(i);
  20.             }
  21.         }
  22.         return list;
  23.     }finally {
  24.         RedisUtil.returnResource(jedis);
  25.     }
  26. }
複製程式碼