短视频点赞场景下,Set 更稳妥;Bitmap 虽省内存但必须将用户 ID 重映射为紧凑逻辑 ID,否则因 offset 过大导致内存爆炸,且无法反查用户、不支持原子互斥操作。

直接说结论:短视频场景下,Set 更稳妥,Bitmap 有明显收益但必须严控 offset 范围,否则极易因用户 ID 稀疏或错配导致内存爆炸。
为什么短视频点赞不能无脑用 Bitmap
Bitmap 的 offset 是按用户 ID 当作数组下标用的。如果系统里用户 ID 是自增主键,且从 1 开始、连续、不跳号,那 SETBIT video:123 456789 1 是安全的;但现实中短视频平台的用户 ID 往往是雪花算法生成(如 1298473029485720394),直接当 offset 会导致 Redis 分配数百 MB 甚至 GB 内存——这不是“省空间”,是制造事故。
真实踩坑案例:某平台误将商户 ID(起始值设为 21 亿)作为 offset,单个 SETBIT 就触发 Redis 分配约 250MB 内存,集群迅速 OOM。
-
offset超过 1000 万时,内存占用 ≈offset / 8 / 1024 / 1024MB,不是线性增长,是按最大下标向上取整分配整块内存 - 无法用
GETBIT判断“某个大 ID 是否存在”,只能确认“该偏移位是否为 1”,但中间大量 0 位已占满内存 - 不支持反查:“查出所有给视频 123 点过赞的用户”这种需求,
Bitmap完全做不到
Set 实现点赞/踩的最小可行结构
用两个独立 key 分离点赞和点踩,避免逻辑耦合:
- 点赞集合:
like:video:123 - 点踩集合:
dislike:video:123
关键操作全部基于原生命令,不依赖业务层判断状态:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用户点赞:
SADD like:video:123 456789(重复执行无副作用) - 用户点踩:
SADD dislike:video:123 456789 - 取消点赞:
SREM like:video:123 456789 - 检查是否点赞:
SISMEMBER like:video:123 456789→ 返回 1 或 0 - 互斥校验(点赞后不能点踩)需业务层控制,Redis 不自动拦截
总点赞数用 SCARD like:video:123,毫秒级返回;点踩数同理。无需额外计数器,也无需担心并发写覆盖。
Bitmap 可用但必须重映射 offset
真要用 Bitmap 节省内存,唯一安全路径是把原始用户 ID 映射成紧凑、连续、可控的整数 ID(即“逻辑 ID”),再作为 offset:
- 映射方式必须是确定性单向函数,例如
user_id % 10000000不行(冲突高),推荐用布隆过滤器 + 自增序列或预分配 ID 池 - 映射表本身要持久化(MySQL 或 Redis Hash),且保证原子性:先查映射、再
SETBIT、失败则回滚映射申请 -
BITCOUNT video:123只能统计总点赞数,无法区分“谁点了”,所以必须搭配另一个结构(如 Set 或 Hash)存用户行为快照用于审计或召回 - 上线前必须压测:模拟最高用户 ID 对应的
offset,验证 Redis 内存分配与响应延迟
别忽略的细节:点赞/踩状态同步与一致性
前端双态按钮(赞/踩)切换时,后端不能只依赖 Redis 返回结果做 UI 更新——SISMEMBER 和 SCARD 是两个命令,中间可能有其他用户操作,造成视觉错乱。
更可靠的做法是:一次请求完成状态判定 + 增删动作 + 总数读取,用 Lua 脚本封装:
eval "local liked = redis.call('sismember', KEYS[1], ARGV[1]) local disliked = redis.call('sismember', KEYS[2], ARGV[1]) if liked == 1 then redis.call('srem', KEYS[1], ARGV[1]) elseif disliked == 0 then redis.call('sadd', KEYS[1], ARGV[1]) end return {redis.call('scard', KEYS[1]), redis.call('scard', KEYS[2])}" 2 like:video:123 dislike:video:123 456789
这样点赞/点踩互斥、总数准确、网络往返仅 1 次。而 Bitmap 方案目前没有等效的原子多操作支持,必须靠客户端协调,风险更高。

















