用Set而非String或Hash,因其天然支持SADD(自动去重)、SISMEMBER(O(1)判存)、SCARD(实时计数)且三者均原子;必须用SADD返回值(0或1)直接判断是否首次点赞,禁用先查后添;Set不支持key级TTL,需按业务拆分key或定时清理;注意各客户端对SADD返回值封装差异及WRONGTYPE风险。

为什么用 Set 而不是 String 或 Hash?
因为点赞本质是「用户对某资源的单次动作」,核心约束是“同一用户不能重复点同一条评论”,即去重 + 成员判定。String 只能存一个值,无法表达“谁点了”;Hash 虽可存 user_id → 1,但没有原生的成员存在性原子判断;而 SET 天然支持 SADD(自动去重)、SISMEMBER(O(1) 判存)、SCARD(实时计数),三者都无需加锁或事务。
SADD + SISMEMBER 组合才是防重关键
不能只靠 SISMEMBER 查询再决定是否添加——中间存在竞态窗口。必须用原子操作闭环:
-
SADD comment:likes:123 456:添加用户 ID 456 到评论 123 的点赞集合,若已存在则不重复插入,返回值为 1(新增)或 0(已存在) - 根据返回值直接判断是否为首次点赞,避免二次查询
- 不要写成「先
SISMEMBER再SADD」,这在高并发下必然漏判
过期时间不能设在 Set 上,得靠业务层兜底
SET 类型本身不支持 key 级 TTL,所以不能指望「给整个点赞集合设 24 小时过期」。常见错误是误以为 SETEX 可用于 Set 结构——它只适用于 String。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
正确做法:
- 按业务规则拆分 key 生命周期,例如「每日限点一次」:key 设为
comment:likes:123:20260903,日期部分参与构成 key - 「永久有效」场景(如微博点赞):不设过期,靠业务逻辑清理(如取消点赞用
SREM) - 若需自动清理历史数据,用定时任务扫描并删除过期日期的 key,而非依赖 Redis 自动过期
注意 SADD 返回值语义和客户端适配
不同语言客户端对 SADD 返回值封装不一致,容易误判:
- Redis 原生命令返回被成功添加的元素个数(0 或 1),不是布尔值
- Jedis 的
sadd(String key, String... members)返回Long,需判断是否等于1L - Redisson 的
RSet.add()返回Boolean,但要注意它是包装后的结果,非原始协议返回 - Spring Data Redis 的
opsForSet().add(key, value)同样返回Long,别当成 true/false 用 if 判断
最容易被忽略的是:当 key 不存在时,SADD 会自动创建 set 并添加;当 key 存在但类型不是 set 时,命令直接报错 WRONGTYPE Operation against a key holding the wrong kind of value——上线前务必确认 key 没被其他逻辑误写为 String。

















