跳到主要内容

用户签到与UA统计

在现代应用中,用户签到和 UV 统计是常见的功能,它们对于了解用户行为、优化产品体验以及进行数据分析都具有重要意义。本文将对黑马点评项目中用户签到和 UV 统计的相关内容进行深入探讨和优化。

BitMap用户签到

BitMap 用户签到简介

  • 签到信息统计方式:我们采用按月统计用户签到信息的方式,将签到记录标记为 1,未签到记录为 0。通过将每一个 bit 位与当月的每一天相对应,形成了一种映射关系。这种用 0 和 1 来标示业务状态的思路被称为位图(BitMap)。
  • Redis 实现方式:在 Redis 中,BitMap 是利用 string 类型数据结构来实现的。由于 string 类型的限制,BitMap 的最大上限是 512M,转换为 bit 则是 2^32 个 bit 位。

1653824498278.png

  • 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 出现的位置。

代码:

        @Override
public Result sign() {
Long userId = UserHolder.getUser().getId();
String keySuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = USER_SIGN_KEY + userId + keySuffix;
// 获取今天是第几天
int dayOfMonth = LocalDateTime.now().getDayOfMonth();
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
return Result.ok();
}

在业务逻辑中,

  1. 首先获取当前用户的 ID,并根据当前时间生成一个键的后缀(格式为 ":yyyyMM")。
  2. 然后,构建完整的键(USER_SIGN_KEY + userId + keySuffix)
  3. 接下来,获取今天是本月的第几天,并使用stringRedisTemplate.opsForValue().setBit方法将对应位置的 bit 值设置为 1,表示用户在这一天签到。
  4. 最后,返回签到成功的结果。

签到统计

  • 统计方式:从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,即连续签到天数。
  • 获取签到数据的方法:使用BITFIELD key GET u[dayOfMonth] 0命令来获取本月到今天为止的所有签到数据。
        @Override
public Result signCount() {
Long userId = UserHolder.getUser().getId();
LocalDateTime now = LocalDateTime.now();
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = USER_SIGN_KEY + userId + keySuffix;
int dayOfMonth = now.getDayOfMonth();
List<Long> result = stringRedisTemplate.opsForValue().bitField(
key,
BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
);
if (result == null || result.isEmpty()) {
return Result.ok(0);
}
Long num = result.get(0);
if (num == null || num == 0) {
return Result.ok(0);
}
int count = 0;
while (true) {
if ((num & 1) == 0) {
break;
} else {
count++;
}
num >>>= 1;
}
return Result.ok(count);
}

在业务逻辑中,同样根据用户 ID 和当前时间生成键,然后获取今天是本月的第几天。使用stringRedisTemplate.opsForValue().bitField方法执行BITFIELD命令获取签到数据。如果结果为空或为空列表,则返回签到次数为 0。否则,获取结果中的第一个数,并通过位运算逐位判断,统计连续签到的天数。最后,返回签到次数的结果。

UA统计

  1. UV 和 PV 的概念
    • UV(Unique Visitor):全称独立访客量,指通过互联网访问、浏览该网页的自然人。在 1 天内,同一个用户多次访问该网站,只记录 1 次。
    • PV(Page View):全称页面访问量或点击量,用户每访问网站的一个页面,记录 1 次 PV。如果用户多次打开页面,则记录多次 PV。PV 通常用来衡量网站的流量。
  2. Hyperloglog 简介
    • 算法来源:Hyperloglog(HLL)是从 Loglog 算法派生的一种概率算法,主要用于确定非常大的集合的基数,而不需要存储集合中的所有值。https://juejin.cn/post/6844903785744056333#heading-0
    • Redis 实现:在 Redis 中,HLL 是基于 string 结构实现的。
    • 内存和误差:单个 HLL 的内存永远小于 16kb,其测量结果是概率性的,有小于 0.81%的误差。
  3. 在 UV 统计中的应用 在统计网站的 UV 时,可以使用 HLL 来存储访问用户的信息。当用户访问网站时,将用户的标识添加到 HLL 中。然后,使用 HLL 的相关命令来统计 UV。由于 HLL 具有高效的空间利用率和较低的误差率,适用于处理大规模数据的基数统计,尤其在对精度要求不是特别高的场景中,能够有效地节省内存并快速得出近似的统计结果。