Redis Bitmap 实现北极星日淘用户签到与活跃度统计(极致省内存)
摘要:北极星日淘需要统计用户每日签到、月度活跃度、连续签到天数等运营数据,传统数据库存储每条签到记录数据冗余大、内存占用高、查询效率低。本文基于Redis Bitmap位图结构,实现百万级用户签到数据的极致轻量化存储,单月百万用户签到数据仅占用数MB内存,附完整Java源码与活跃度统计逻辑。
关键词:Redis Bitmap;位图;用户签到;活跃度统计;内存优化;北极星日淘
一、传统方案痛点
传统用户签到方案采用MySQL单用户单日单条记录存储,北极星数万用户每月会产生百万级数据量,数据表持续膨胀,查询月度签到、连续签到需要大量SQL统计,性能极差、存储成本极高。而Redis Bitmap通过二进制位存储数据,1个字节可存储8个用户签到状态,极致节省内存,适配海量用户签到统计场景。
二、Bitmap技术原理
Bitmap本质是二进制位数组,每一个bit位仅存储0和1两种状态,0代表未签到、1代表已签到。以日期为key、用户ID偏移量为bit下标,精准记录用户每日签到状态。支持快速签到、签到撤销、签到总数统计、连续签到统计、活跃度筛选,操作效率极高。
三、核心代码实现
@Service
public class UserSignService {
@Resource
private RedisTemplate<String,Object> redisTemplate;
// 北极星用户签到key前缀:polar:sign:yyyyMMdd
private static final String SIGN_KEY_PREFIX = "polar:sign:";
// 用户签到
public Boolean userSign(Long userId, String date) {
String key = SIGN_KEY_PREFIX + date;
// 将用户ID作为偏移量,设置bit位为1
return redisTemplate.opsForValue().setBit(key, userId, true);
}
// 查询用户当日是否签到
public Boolean isSign(Long userId, String date) {
String key = SIGN_KEY_PREFIX + date;
return redisTemplate.opsForValue().getBit(key, userId);
}
// 统计用户月度签到总天数
public Long countMonthSign(Long userId, List<String> monthDateList) {
long count = 0;
for (String date : monthDateList) {
if (isSign(userId, date)) {
count++;
}
}
return count;
}
// 统计当日总签到人数
public Long countDaySignUser(String date) {
String key = SIGN_KEY_PREFIX + date;
// 统计bit位为1的总数
return redisTemplate.opsForValue().bitCount(key);
}
}
四、业务落地优化
基于Bitmap实现北极星日淘签到体系,新增签到积分、连续签到奖励、月度活跃度排行功能,数据存储内存占用降低99%以上,签到查询、统计接口响应时间压缩至10ms以内。同时设置key过期时间,自动清理过期签到数据,避免Redis内存堆积,保障服务长期稳定运行。
五、总结
Redis Bitmap是签到、活跃度、在线统计类场景的最优解决方案,极致节省内存、超高读写性能。本次落地北极星日淘用户签到业务,完美解决传统数据库存储冗余、统计缓慢的问题,为平台用户运营、积分体系、活跃度排行提供高效技术支撑,方案可复用至各类用户统计场景。
