Redis-用户签到&UV统计

一、用户签到

1.1 BitMap用法

我们按月来统计用户签到信息,签到记录为1,未签到记录为0
Redis-用户签到&UV统计_第1张图片
把每一个bit位对应当月的每一天,形成了映射关系。用0和1表示业务状态,这种思路就称为位。Redis中是用利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是 2^32个bit位。

BitMap的操作命令有:

SETBIT: 向指定位置(offset)存入一个0或1;
GETBIT: 获取指定位置(offset)的bit值;
BITCOUNT: 统计BitMap中值为1的bit位的数量;
BITFIELD: 操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值;
BITFIELD_RO: 获取BitMap中bit数组,并以十进制形式返回;
BITOP: 将多个BitMap的结果做位运算(与、或、异或);
BITPOS: 查找bit数组中指定范围内第一个0或1出现的位置;

1.2 签到功能

实现签到接口,将当前用户当天签到信息保存到Redis中

public Result sign() {
   // 1. 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    // 2. 获取日期,拼接key
    LocalDateTime now = LocalDateTime.now();
    String key = USER_SIGN_KEY +
            userId +
            now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    // 3. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    // 4. 写入 redis SETBIT key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.ok();
}

1.3 签到统计

问题1:什么叫做连续签到天数?

从最后一次签到开始向前统计,知道遇到第一次未签到为止,计算总的签到次数,就是连续签到次数
在这里插入图片描述

问题2:如何得到本月到今天为止的所有签到数据?

在这里插入图片描述

问题3:如何从后向前遍历每个bit位?

与1做与运算,就能得到最后一个bit位。
随后右移1位,下一个bit位就成为了最后一个bit位。

@Override
public Result signCount() {
    // 1. 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    // 2. 获取日期,拼接key
    LocalDateTime now = LocalDateTime.now();
    String key = USER_SIGN_KEY +
            userId +
            now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    // 3. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    // 4. 获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0
    List<Long> results = stringRedisTemplate.opsForValue().bitField(
            key,
            BitFieldSubCommands.create()
                    .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                    .valueAt(0)
    );
    if (Objects.isNull(results) || CollectionUtils.isEmpty(results)) {
        return Result.ok(0);
    }
    Long num = results.get(0);
    if (Objects.isNull(num) || num == 0) {
        return Result.ok(0);
    }
    // 5. 循环遍历
    int count = 0;
    while (true) {
        // 6.1. 让这个数字与1做与运算,得到数字的最后一个bit位 // 判断这个bit位是否为0
        if ((num & 1) == 0) {
            // 如果为0,说明未签到,结束
            break;
        } else {
            // 如果不为0,说明已签到,计数器+1
            count++;
        }
        // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
        num >>>= 1;
    }
    return Result.ok(count);
}

二、UV统计

2.1 HyperLogLog用法

首先,我们搞懂两个概念:

  • UV: 全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。
  • PV: 全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1此PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。

UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是每个访问的用户都保存到Redis中,数据量会非常恐怖

2.2 实现UV统计

我们直接利用单元测试,向HyperLogLog中添加100万条数据,看看内存占用和统计效果如何:

@Test
void testHyperLogLog() {
     String[] values = new String[1000];
     int j = 0;
     for (int i = 0; i < 1000000; i++) {
         j = i % 1000;
         values[j] = "user_" + i;
         if (j == 999) {
             // 发送到Redis
             stringRedisTemplate.opsForHyperLogLog().add("hl2", values);
         }
     }
     // 统计数量
     Long count = stringRedisTemplate.opsForHyperLogLog().size("hl2");
     System.out.println("count = " + count);
 }

运行结果:

count = 997593

查看内存(命令:info memory)

  • 插入数据之前
    Redis-用户签到&UV统计_第2张图片
  • 插入数据之后
    Redis-用户签到&UV统计_第3张图片
  • 计算
    (1396913 - 1366928)/ 1024
    Redis-用户签到&UV统计_第4张图片

2.3 总结

  • HyperLogLog的作用:
    • 做海量数据的统计工作
  • HyperLogLog的优点:
    • 内存占用极低;
    • 性能非常好;
  • HyperLogLog的缺点:
    • 有一定的误差

你可能感兴趣的:(SpringCloud,redis,uv,java)