redis有序集合用zadd和zrevrange实现排行榜,score需为整数以避免浮点精度问题;zincrby保障并发安全,禁止读-改-写模式。

用 ZADD 和 ZREVRANGE 维护实时排行榜
Redis 的有序集合(Sorted Set)天然适合做排行榜:成员唯一、按 score 排序、支持范围查询。Spring Boot 里直接用 RedisTemplate 调用原生命令最稳,别迷信封装过深的二次抽象。
关键点是 score 必须能反映排名依据——比如用户积分、点击数、热度值。score 为整数时排序更可靠(浮点精度问题在高并发下可能引发同分错序)。
-
ZADD插入或更新成员:redisTemplate.opsForZSet().add("rank:score", userId, score),重复调用会自动更新 score - 查 Top N 用
ZREVRANGE(倒序,分数高在前):redisTemplate.opsForZSet().reverseRange("rank:score", 0, 9) - 查某用户具体排名用
ZREVRANK:redisTemplate.opsForZSet().reverseRank("rank:score", userId),注意返回是 0-based 索引
避免 ZINCRBY 在并发场景下覆盖写
如果多个服务实例同时对同一用户的 score 做增量更新(比如每次点赞 +1),直接用 ZINCRBY 是安全的——Redis 原子性保证不会丢数据。但如果你用 ZADD 先读再算再写,就必然有竞态。
常见错误是写成「get → calc → set」三步,这在分布式环境下毫无意义。必须让计算逻辑下沉到 Redis 侧。
- 用
ZINCRBY替代读-改-写:redisTemplate.opsForZSet().incrementScore("rank:score", userId, delta) - delta 必须是数字类型,不能是字符串;负值表示扣分
- 如果需要条件更新(如“仅当当前 score ZINCRBY 不支持条件
分页查榜慎用 ZREVRANGEBYSCORE
按分数区间分页(比如查 score 在 1000–2000 之间的用户)看似合理,但实际容易翻车:score 可能大量重复,导致分页结果不稳定(同分用户顺序不固定)、跳页或漏数据。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
真正稳定的分页只有一种方式:用上一页最后一名的 score + member 作为游标,配合 ZREVRANGEBYSCORE 的 WITHSCORES 和 LIMIT 参数。
- 首次请求:
ZREVRANGE rank:score 0 9 WITHSCORES - 后续请求需记录上一页末尾的 score 和 member:
ZREVRANGEBYSCORE rank:score (last_score last_member LIMIT 0 10(注意(表示开区间) - Spring Boot 中需手动拼接 Lua 或用
RedisTemplate.execute()调用带游标的脚本,别依赖reverseRangeByScore的简单封装
排行榜数据过期与冷热分离怎么设 TTL
Redis 内存贵,排行榜不能永久存。但 EXPIRE 不能直接作用于 sorted set 的某个 member,只能对整个 key 设 TTL。
如果所有用户共用一个 key(如 rank:score),设 TTL 就意味着整个榜一起失效——这通常不是你想要的。更合理的做法是按维度隔离 key:
- 按天分表:
rank:score:20240601,每天凌晨用EXPIRE设 7 天 TTL - 按业务域分表:
rank:video:hot、rank:video:week,各自独立 TTL - 绝对不要给全局排行榜 key 设置短 TTL(比如 1 小时),会导致频繁重建和抖动
冷数据归档建议用 ZRANGE 批量导出后删 key,而不是靠过期自动清理——你无法控制过期时间点,高峰期可能触发大量淘汰操作影响性能。










