Redis 如何实现一个排行榜

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

Redis实现排行榜的核心是利用 有序集合(Sorted Set,简称ZSet) 数据结构,其天生具备“键-分数-成员”的映射关系,支持按分数排序、快速查询排名,完美匹配排行榜的核心需求(如积分排名、热度排名等)。以下从基础实现到进阶优化,详细讲解完整流程。

一、核心原理:ZSet的特性

ZSet 由“成员(member)”和“分数(score)”两部分组成,核心特性如下,也是实现排行榜的关键:

  • 分数(score):可自定义(如用户积分、文章阅读量、商品销量等),支持整数、浮点数,作为排序依据;
  • 自动排序:Redis会自动根据score的大小,对成员进行升序/降序排列,无需手动维护排序逻辑;
  • 快速操作:支持O(logN)时间复杂度的插入、更新、查询排名、获取区间成员等操作,适合高并发场景;
  • 去重性:成员(member)唯一,同一成员多次插入会覆盖其分数,避免重复排名。

二、基础排行榜实现(以“用户积分排行榜”为例)

假设需求:实现一个用户积分排行榜,支持用户积分增减、查询用户排名、查询TopN用户、查询用户周边排名,核心操作如下:

1. 初始化/添加用户积分(ZADD)

使用ZADD命令,向有序集合中添加用户(member)和对应的积分(score),若用户已存在,则更新其积分。

# 语法:ZADD 排行榜key 分数 成员 [分数 成员 ...]
# 示例:添加3个用户,积分分别为100、200、150
ZADD user_ranking 100 user1 200 user2 150 user3

说明:若需批量添加/更新,可一次性传入多组“分数+成员”,效率更高。

2. 更新用户积分(ZINCRBY)

实际场景中,积分常需动态增减(如用户完成任务加积分、违规减积分),使用ZINCRBY命令,无需先查询再更新,原子性操作,避免并发问题。

# 语法:ZINCRBY 排行榜key 增减量 成员
# 示例1:给user1增加50积分(积分变为150)
ZINCRBY user_ranking 50 user1
# 示例2:给user2减少30积分(积分变为170)
ZINCRBY user_ranking -30 user2

3. 查询TopN排行榜(ZREVRANGE)

排行榜通常按分数降序排列(分数越高,排名越靠前),使用ZREVRANGE命令(倒序查询),获取前N名用户及对应分数。

# 语法:ZREVRANGE 排行榜key 起始索引 结束索引 [WITHSCORES]
# 说明:起始索引从0开始,结束索引为N-1;WITHSCORES表示同时返回分数
# 示例:查询Top2用户(含积分)
ZREVRANGE user_ranking 0 1 WITHSCORES
# 返回结果:1) "user2" 2) "170" 3) "user1" 4) "150"

补充:若需升序排列(分数越低排名越靠前),使用ZRANGE命令,语法与ZREVRANGE一致。

4. 查询单个用户的排名(ZREVRANK)

使用ZREVRANK命令,查询指定用户的排名(倒序,排名从0开始,0表示第一名),配合ZCORE命令可获取用户当前积分。

# 语法1:ZREVRANK 排行榜key 成员(查询排名)
# 示例:查询user1的排名
ZREVRANK user_ranking user1  # 返回1(第二名)

# 语法2:ZSCORE 排行榜key 成员(查询积分)
ZSCORE user_ranking user1    # 返回"150"

5. 查询用户周边排名(ZREVRANGEBYRANK)

实际场景中,常需展示“用户自身排名+前后N名”(如用户排名第5,展示3-7名),使用ZREVRANGEBYRANK命令,按排名区间查询。

# 语法:ZREVRANGEBYRANK 排行榜key 起始排名 结束排名 [WITHSCORES]
# 示例:查询user1(排名1)前后1名,即排名0-2的用户
ZREVRANGEBYRANK user_ranking 0 2 WITHSCORES

三、进阶功能:满足复杂场景需求

1. 分页查询排行榜

当排行榜用户数量较多时,需支持分页,利用ZREVRANGE的索引区间实现,无需一次性查询所有数据,提升性能。

# 示例:分页查询,每页10条,查询第2页(索引10-19)
ZREVRANGE user_ranking 10 19 WITHSCORES

2. 按分数区间筛选排名

若需筛选“积分在100-200之间”的用户排名,使用ZREVRANGEBYSCORE命令,按分数区间查询,支持闭区间/开区间。

# 语法:ZREVRANGEBYSCORE 排行榜key 最高分 最低分 [WITHSCORES] [LIMIT 起始偏移量 数量]
# 示例:查询积分100-170之间的用户,取前2个
ZREVRANGEBYSCORE user_ranking 170 100 WITHSCORES LIMIT 0 2

3. 排行榜过期清理

若排行榜有有效期(如每日排行榜、每周排行榜),可给ZSet设置过期时间(EXPIRE),到期自动清理,无需手动删除。

# 示例:设置每日排行榜,24小时后过期
EXPIRE user_ranking_daily 86400

4. 多维度排行榜(如按地区、按分类)

若需实现多维度排行榜(如“北京地区用户排行榜”“游戏分类积分榜”),可通过自定义ZSet的key区分,例如:

# 北京地区用户排行榜
ZADD user_ranking_beijing 180 user4 160 user5
# 游戏分类积分榜
ZADD user_ranking_game 220 user6 190 user7

四、性能优化建议

  • 避免高频全量查询:尽量使用分页、区间查询(ZREVRANGE、ZREVRANGEBYSCORE),减少全量数据返回;
  • 控制ZSet大小:若排行榜数据量极大(如百万级用户),可定期清理低分数用户(使用ZREMRANGEBYScore删除分数低于阈值的成员),避免占用过多内存;
  • 利用缓存预热:对于高频访问的TopN排行榜,可定期将结果缓存到普通字符串(String)中,减少ZSet查询压力(需注意缓存更新时机);
  • 原子操作保证一致性:所有积分更新、排名查询均使用Redis原生命令(ZINCRBY、ZREVRANK等),避免多步操作导致的并发问题。

五、常见误区

  • 混淆排名索引:ZREVRANK返回的排名从0开始(0是第一名),需在业务层转换为从1开始的排名(如返回0时,展示为第1名);
  • 分数精度问题:若分数为浮点数,需注意Redis的浮点数精度(基于双精度浮点数),避免因精度误差导致排名异常;
  • 忽略去重性:ZSet的member唯一,若需同一用户在多个维度排名,需使用不同的ZSet key,不可共用一个。

六、总结

Redis实现排行榜的核心是利用ZSet的“分数排序+快速操作”特性,基础场景可通过ZADD、ZREVRANGE、ZINCRBY等命令快速实现;复杂场景(分页、多维度、过期清理)可通过扩展key设计、结合Redis其他命令优化。其优势在于高性能、高并发支持,适合各类实时排行榜场景(如用户积分、商品销量、内容热度等)。

ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花

Redis常用的数据结构 文章被收录于专栏

Redis 作为高性能键值数据库,核心在于丰富的数据结构。本专栏聚焦String、Hash、List、Set、ZSet、Bitmap、HyperLogLog 等常用类型,从底层原理、使用场景到实战示例,清晰讲解每种结构的优缺点与最佳实践。帮你快速掌握如何用对数据结构,提升缓存、限流、排行榜、消息队列等业务场景的开发效率,写出更稳定、高效的 Redis 应用。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务