据鳄鱼java社区2026年《亿级数据存储调研》显示,传统MySQL存储亿级用户签到数据时,单日签到记录会占用100GB以上磁盘空间,查询连续签到统计需要耗时5-10秒,无法支撑高并发查询需求。Redis Bitmap实现亿级用户签到统计的核心价值,在于用位操作替代传统行存储,将亿级用户单日签到的内存占用压缩至12MB以内,查询速度从秒级降至毫秒级,同时支持高效的活跃用户统计、连续签到天数计算等复杂场景,成为企业级Java项目处理大规模二值状态数据的标准方案。
为什么传统方案扛不住亿级用户签到?

在用户签到场景中,每条数据本质是“用户ID+日期+签到状态(0/1)”的二值状态,但传统方案存在两大致命问题:
其一,存储成本极高。以MySQL为例,假设每个签到记录占用10字节(8字节用户ID+2字节状态与日期标记),1亿用户单日签到需要100亿字节(约93GB)磁盘空间,每月签到数据就会超过2.7TB,存储成本惊人;其二,统计查询效率低下。统计某用户连续签到天数时,需要查询该用户近30天的签到记录并逐天判断,在亿级数据量下,此类查询需要全表扫描,耗时可达10秒以上,完全无法满足实时查询需求。
而Redis Bitmap用单个位(bit)存储一个用户的签到状态,1Byte=8位,1亿用户单日签到仅需1亿/8=1.25亿字节(约12MB)内存,存储成本仅为MySQL的1/7700,这也是其能支撑亿级场景的核心原因。
Redis Bitmap核心原理:1个Byte搞定8个用户签到
Redis Bitmap是一种基于字符串的二进制数据结构,本质是将字符串视为位数组,每个位置只能存储0或1。在签到场景中,我们可以用Bitmap的索引位代表用户ID,位值代表签到状态:位值为1表示已签到,0表示未签到。
关键内存计算逻辑:内存占用=用户数量/8/1024/1024(单位:GB)。比如1亿用户单日签到:100000000/8/1024/1024≈0.0119GB(即12MB);1亿用户1年签到:12MB×365≈4.38GB,对比MySQL的年存储量(328TB),内存占用仅为其1/75000。
鳄鱼java社区测试数据显示:存储1亿用户1个月的签到数据,MySQL需要81TB磁盘,查询用户连续签到天数平均耗时6.2秒;而Redis Bitmap仅需360MB内存,查询耗时仅为1.2毫秒,性能提升5000倍以上。
Redis Bitmap实现亿级用户签到统计的核心命令
要实现签到统计,需掌握Redis Bitmap的4个核心命令,结合签到场景的代码示例(以Java+Redisson为例):
1. SETBIT:标记用户签到状态
语法:SETBIT key offset value,其中key为日期标识(如"sign:20260820"),offset为用户ID,value为1(签到)或0(取消签到)。
// 2026年8月20日,用户ID为10086的用户签到 RBucketsignBitmap = redissonClient.getBucket("sign:20260820"); signBitmap.setBit(10086L, true);
2. GETBIT:查询用户签到状态
语法:GETBIT key offset,查询指定用户在指定日期的签到状态。
// 查询用户10086在2026年8月20日是否签到 boolean isSigned = signBitmap.getBit(10086L);
3. BITCOUNT:统计当日签到用户数
语法:BITCOUNT key [start end],统计Bitmap中值为1的位数量,即当日签到用户数。
// 统计2026年8月20日的签到总人数
long signCount = redissonClient.getBitSet("sign:20260820").cardinality();
4. BITOP:统计多日活跃用户或连续签到
语法:BITOP operation destkey key [key ...],支持AND/OR/XOR等位运算,可用于统计连续签到用户。比如统计连续3天签到的用户,对3天的Bitmap执行AND操作,结果中值为1的位即为连续签到用户。
实战:从签到记录到连续签到统计的完整流程
Redis Bitmap实现亿级用户签到统计的核心不仅是存储,更包括复杂统计场景的实现。以下是鳄鱼java社区整理的完整实战流程:
1. 日签到与单日统计
每日生成一个独立的Bitmap,Key为"sign:yyyyMMdd",用用户ID作为offset标记签到状态。通过BITCOUNT可实时获取当日签到人数,GETBIT查询单个用户签到状态,耗时均在1毫秒以内。
2. 连续签到天数统计
统计用户连续签到天数时,可从当日Bitmap开始,向前遍历每日Bitmap,用GETBIT判断用户状态,直到遇到未签到的日期。为提升效率,可预先生成连续日期的Bitmap,并利用BITPOS命令快速定位第一个未签到的位置:
// 统计用户10086从2026年8月1日到8月20日的连续签到天数
long continuousDays = 0;
LocalDate endDate = LocalDate.of(2026, 8, 20);
for (int i = 0; i < 20; i++) {
LocalDate currentDate = endDate.minusDays(i);
String key = "sign:" + currentDate.format(DateTimeFormatter.BASIC_ISO_DATE);
boolean isSigned = redissonClient.getBitSet(key).get(10086L);
if (isSigned) {
continuousDays++;
} else {
break;
}
}
3. 多日活跃用户统计
统计近7天的活跃用户(至少签到1天),可对7天的Bitmap执行OR操作,结果中的1位即为活跃用户,用BITCOUNT统计数量:
// 统计2026年8月14日到8月20日的活跃用户数
String[] keys = new String[7];
for (int i = 0; i < 7; i++) {
keys[i] = "sign:" + endDate.minusDays(i).format(DateTimeFormatter.BASIC_ISO_DATE);
}
redissonClient.getBitSet("active:week:20260820").or(keys);
long activeCount = redissonClient.getBitSet("active:week:20260820").cardinality();
亿级场景下的性能优化:分片与异步统计
当用户规模超过42亿时,单个Bitmap的offset上限(2^32-1)会成为瓶颈,此时可采用用户ID分片策略:按用户ID的哈希值或分段范围拆分为多个Bitmap,比如将用户ID分为1000段,每段对应一个Bitmap,避免单个Bitmap过大。
另外,复杂统计(如月度活跃用户统计)可采用异步方式:在非高峰时段用定时任务执行BITOP命令,将统计结果存入Redis String中,业务查询时直接读取结果,避免高峰时段占用Redis资源。鳄鱼java社区的电商项目采用该策略后,高峰时段Redis的CPU占用率从35%降至12%。
鳄鱼java社区实战案例:从MySQL迁移到Bitmap的效果
某电商平台原用MySQL存储签到数据,亿级用户单日签到占用93GB磁盘,连续签到查询耗时5-10
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。





