Bitset用1位二进制表示用户状态,100万用户仅需约122KB,较Redis String节省99%内存;ID作偏移量定位,Redis通过SETBIT/GETBIT/BITCOUNT三命令高效操作。

用二进制位数组(Bitset)存百万级用户状态,核心就一条:**每个用户对应一个 bit,1 表示“已激活/已登录/已签到”,0 表示“未激活/离线/未签到”**。不存 ID、不建索引、不序列化对象,只用 1 位二进制,空间直接压到最低,查询快到极致。
为什么能省下 99% 的内存?
对比传统方式:
- 用 Redis String 存 100 万个用户登录状态(key=user:123, value=1)→ 每个 key-value 至少占几十字节,总内存轻松破百 MB;
- 用 Bitset 存 → 100 万个用户只需 100 万 bit = 1000000 ÷ 8 ÷ 1024 ≈ 122 KB;
- 存 1 亿用户也只要约 12.5 MB(见 Redis 官方数据),比 Set 小 30 倍以上。
怎么定位某个用户的状态?
假设用户 ID 是非负整数(如 0、1、1001、999999),那就直接把它当“偏移量(offset)”用:
- 用户 ID = 1001 → 查第 1001 位(从 0 开始计);
- 计算所在字节位置:byteIndex = 1001 ÷ 8 = 125(取整);
- 计算在该字节中哪一位:bitOffset = 1001 % 8 = 1;
- 取出字节后,用
byteValue & (1 判断是否为 1。
Redis 中三行命令搞定
不用自己手写位运算,Redis 原生支持:
-
SETBIT user:online 1001 1→ 标记用户 1001 已上线; -
GETBIT user:online 1001→ 返回 1 或 0,毫秒级响应; -
BITCOUNT user:online→ 秒出当前在线总人数(自动统计所有 1 的个数)。
实际使用要注意什么?
几个关键细节决定能不能落地:
- ID 必须是数字且尽量连续或可映射:若用户 ID 是字符串(如 "u_abc123"),需先做哈希或映射成整数,避免稀疏浪费空间;
- 最大 ID 决定总空间:若最大 ID 是 5000 万,Bitset 就要分配 5000 万 bit ≈ 6.1 MB,哪怕只用其中 10 万个;
- 超出范围的 GETBIT 默认返回 0:不会报错,但需确认业务能否接受“未设置即视为未激活”;
-
不支持直接遍历所有“1”的位置:想拉出全部在线用户 ID,得用
BITPOS分段扫描,或搭配其他结构做补充。


















