Redis - bitmap 使用案例

Redis - bitmap 使用案例

  1. 【基本操作】
    1. setbit KEY OFFSET VALUE  

                  举例 : setbit month_au_202106 20210601 '1',这里需要注意的一点是offset是按照比特位设置的,与bitcount

  1. getbit KEY OFFSET   

                  举例:getbit month_au_202106 20210601

  1. bitcount KEY

                  举例 :bitcount month_au_202106  

  1. bitcount KEY START END

                  举例 : bitcount month_au_202106 20210601 20210602

                  这里要补充说明的一点是,Redis的setbit修改的是bit位置,而bitcount检查的是byte位置,1byte=8bit,因此在实际使用中需要统一offset的使用,需要再setbit时设置offset * 8,后面案例实现中注意

  1. 【案例应用】
    1. 统计用户活跃状态
    2. 如果允许有误差,可以考虑HyperLogLog类型而不使用BitMap
    3. 需要考虑以下内容  key、offset的设定,key的存活时间                    
Key、offset设置
场景key定义offset 计算

按年或月统计每个用户活跃天数

AU_userid_year 

AU_userid_yearMonth

AU_userid

DateTime.dayOfYear()

DateTime.dayOfMonth()

Days.daysBetween(BASE_DATE,time).getDays()

按年或月统计用户活跃量

AU_year 

AU_yearMonth

AU_userid

calOffset(userId) 自定义一个合适的offset

     这里举例:key 设定格式为 month_au_userId_yearMonth ,offset = (当前时间 - 基准时间)的天数*8,代码如下:

import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeFieldType;
import org.joda.time.DateTimeZone;
import org.joda.time.Days;

/**
 *  redis bitmap 统计用户月活样例代码
 */
public class BitMapMark {

    /** 标记常量 */
    private static final String ACTIVE_MARK = "1";

    /**  基准日期 */
    //private static final DateTime BASE_DATE;
    
    /** 1字节=8bit */
    private static final int BIT_LEN = 8;

    /** 失效时间 */
    int ACTIVE_USER_MONTH_COUNT_EXPIRE = 31*24*3600;

    /**
     * 如果key不按时间进行区分,在计算偏移值时可以指定一个起始时间,用于计算offset
     * 如果key中不考虑用户信息 则可以考虑使用用户ID(如果是数字的话)经过一定运算后用于offset
   static {
        //基准时间20210601 
        BASE_DATE =
                new DateTime()
                .withZone(DateTimeZone.UTC)
                .withYear(2021).withMonthOfYear(6).withDayOfMonth(1).withMillisOfDay(0);
    }*/



    /**
     * 根据用户ID 标识当天状态

     * @param userId
     * @return
     */
    public void incryDailyActive(String userId) {

        Long result = 0L;
        RedisConnect redisConnection = null;
        try {
            if(StringUtils.isNumeric(userId)){
                // TODO conn = 获取redis链接
                DateTime currTime = new DateTime();

                //根据当前时间计算偏移量
                //当前时间 - 基准时间(取当前时间较近的日期)
                //计算偏移
                //  1.是避免浪费空间, 
                //  2.offset * BIT_LEN 是为了在使用bitcount 按范围查询是能够正确对应
                Long timeOffset = getDayOffset(currTime) * BIT_LEN;

                //获取KEY值 "MAU_userID_yearMon"
                String monthKey = getActiveUserMonthKey(userId,currTime);
                redisConnection.setbit(monthKey,timeOffset,ACTIVE_MARK);
                //设置失效时间 根据需要设置过期
//redisConnection.expireKey(monthKey,ACTIVE_USER_MONTH_COUNT_EXPIRE);
            }
        } catch(Exception e){
            e.printStackTrace();
        } finally {
            if(null!=redisConnection){
                //TODO 关闭链接
            }
        }
    }
    
    /**
     * 查询当月累计月活
     * @param userId 
     * @return
     */
    public Long getCurrMonthActive(String userId) {
        Long monthAuCount = 0L;
        try {
            DateTime curr = new DateTime();
            
            //计算查询日期范围
            Long end = calcOffset(curr);
            curr = curr.withField(DateTimeFieldType.dayOfMonth(),1);
            Long start = calcOffset(curr);
            //TODO conn = getRedisConnection(); 获取redis链接
            
            String monthKey = getActiveUserMonthKey(userId,curr);
            monthAuCount = redisConnection.bitcountRange(monthKey,start,end);
            if(null == monthAuCount){
                monthAuCount = 0L;
            }
        } catch(Exception e){
            e.printStackTrace();
        } finally {
            if(null!=redisConnection){
                //TODO 关闭链接
            }
        }
        return monthAuCount;
    }

    /**
     * 获取单个用户月活统计key样例
     * 指定用户指定月份的日活统计
     * 格式:month_au_userId_yearMonth eg: month_au_100_202106
     * @param userId
     * @param curr
     * @return
     */
    private String getActiveUserMonthKey(String userId,DateTime curr) {
        Integer yearMonth = curr.getYear()*100+curr.getMonthOfYear();
        return String.format("month_au_%s_%d",userId,yearMonth);
    }
    
    /**
     * 根据当前时间计算offset样例
     * 要和上面的key 进行对应得到一个合适的偏移
     * 避免空间浪费
     * @param time
     * @return
     */
    private static Long calcOffset(DateTime time){
        return time.getDayOfMonth();
        //return Days.daysBetween(BASE_DATE,time).getDays()*1L;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值