利用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"}