利用Redis实现用户签到、UV统计的功能

用户签到

在Redis中使用位图(BitMap)来存储签到信息,可以大大减小开销。同时在设计redis数据结构时,在key中加上时间、用户id等信息,可以统计该用户在某个时间段内的签到情况。(bitmap最大有2^32位bit位)

具体的数据结构设计为

{"sign:userId:yyyyMM":"bitmapValue"},是以月为单位的签到统计

如果某一天要进行签到,需要进行操作setbit key offset 1,将当天的bit位设置为1即可

统计当月的签到情况bitcount key,即可获得当月的签到天数

bitmap常用操作

setbit [key] [offset] [0/1]  #向指定位置(offset)存入一个0或1
getbit [key] [offset]  #获取指定位置的(offset)bit值
bitcount [key]  #统计bitmap中1的数量
bitfield  #操作(查询、修改、递增)bitmap中bit数据指定位置(offset)的值
#这些操作在java中被封装在opsForValue()方法中

签到业务

业务流程

1、获取当前登录用户

2、获取日期

3、拼接key

4、获取今天是本月第几天

5、写入redis setbit key offset 1

代码实现

UserServiceImpl.userSign()

@Override
public Result userSign() {
    //1、获取当前登录用户
    UserDTO user = UserHolder.getUser();
    //2、获取日期
    LocalDateTime now = LocalDateTime.now();
    String today = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    //3、拼接key
    String key = USER_SIGN_KEY + user.getId() + today;
    //4、获取今天是本月第几天,即redis中的offset
    int dayOfMonth = now.getDayOfMonth();
    //5、写入redis   setbit key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);

    return Result.ok();
}

统计连续签到天数

统计签到天数就是利用bitfield操作实现,由于java中没有封装bitcount功能,只能用bitfield获取无符号数后再进行位运算进行统计

重点关注

1、获得本月到今天为止的签到情况,bitfield key get u[dayOfMonth] 0 ,获取bitmap中从0~dayOfMonth位的无符号整数

2、如何从右向左和每一位bit位做运算,从redis中获取的无符号数与1做与(&)运算,效果是仅获取最右边的一位数

业务流程

1、获取当前登录用户

2、获取日期

3、拼接key

4、获取今天是本月第几天,即redis中的offset

5、获取本月到今天为止的所有签到情况,获得一个无符号整数

6、循环遍历这个无符号数的每一位,位运算

7、判断这一位是否为1

8、如果为1,签到天数count++;连续签到的话如果为0就返回计数结果

代码实现

UserServiceImpl.getSign()

@Override
public Result getSign() {
    //1、获取当前登录用户
    UserDTO user = UserHolder.getUser();
    //2、获取日期
    LocalDateTime now = LocalDateTime.now();
    String today = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    //3、拼接key
    String key = USER_SIGN_KEY + user.getId() + today;
    //4、获取今天是本月第几天,即redis中的offset
    int dayOfMonth = now.getDayOfMonth();

    //5、获取本月到今天为止的所有签到情况,获得一个无符号整数
    List<Long> results = stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
    /**
         * 看业务统计的是连续签到天数还是一个月签到的总天数,
         * 如果是连续签到天数遍历到0直接退出循环即可
         * 在此处实现的是统计当月签到总天数
         */
    int count = 0;
    Long result = results.get(0);
    if(result == null || result == 0) return Result.ok(0);
    //6、循环遍历这个无符号数的每一位,位运算
    while(result != 0L){
        long bit = result & 1;
        //7、判断这一位是否为1
        //8、如果为1,签到天数count++
        if(bit == 1L) count++;
        result >>>= 1;  //long类型的无符号右移
    }

    return Result.ok(count);
}

UV统计

UV(unique visitor)和PV(page view)前者一个用户访问多次只记录一次访问后者用户多次打开网页就记录多次,用于记录网站的流量

使用HyperLogLog进行UV统计:是LogLog派生的概率算法,单个HLL的内存占用永远小于16kb,错误率约为0.81%

常用操作

pfadd [key] [element...]  #向HyperLogLog加入数据
pfcount [key]  #统计HyperLogLog中的数量
pfmerge [destkey] [sourcekey]  #合并多个HyperLogLog的统计数据

对应于java中的这些方法

Blog的UV统计业务流程

在黑马点评业务中对博客的访问量统计,只需要在处理查询单个博客的请求的时候将用户Id增加至HyperLogLog中,在页面发起统计请求时,从redis中获取HyperLogLog的size()返回即可。

redis的数据结构

{"uv:blog:blogId":"hyperLogLogData"}

posted @ 2022-10-22 09:16  CDUT的一只小菜鸡  阅读(639)  评论(0编辑  收藏  举报