首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >​深度剖析排行榜设计:从基础到亿级用户场景

​深度剖析排行榜设计:从基础到亿级用户场景

作者头像
用户1142828
发布2025-03-02 22:48:13
发布2025-03-02 22:48:13
4521
举报

基于数据库:小场景下的实用之选

对于一些数据量较小、业务逻辑相对简单的场景,数据库成为了排行榜设计的首选工具。就拿一个小型比赛的排行榜来说,假设参赛队伍仅有寥寥数支,排行榜表中的数据量始终保持在一个较低的水平。此时,借助数据库的排序功能,如MySQL中的order by语句,便能轻松实现排行榜的基本功能。

基于Redis:高效排行榜的核心引擎

ZSet数据结构基础

ZSet是Redis中的有序集合数据结构,每个元素(member)都关联一个分数(score),ZSet会根据分数对元素进行排序。元素在集合中唯一,但分数可以相同,当分数相同时,按字典序排列。例如,在一个游戏排行榜中,玩家ID是member,玩家的游戏积分就是score。

实现排行榜常用的Redis命令

  1. 添加元素(ZADD):用于向ZSet中添加一个或多个元素。
    • 命令格式ZADD key score member [score member ...]
    • 示例:在一个名为game_rank:202410(表示2024年10月的游戏排行榜)的ZSet中添加玩家数据,假设玩家player1的积分为1000,player2的积分为1200,命令如下:
代码语言:bash
复制
    ZADD game_rank:202410 1000 player1 1200 player2
  • 返回值:返回成功添加的新元素数量(不包括已存在且分数未更新的元素)。例如,如果game_rank:202410原本为空,执行上述命令后,返回(integer) 2 ;若player1已存在且分数不变,仅添加了player2,则返回(integer) 1
  • 增加元素分数(ZINCRBY):对ZSet中指定元素的分数进行增加操作。
    • 命令格式ZINCRBY key increment member
    • 示例:玩家player1在游戏中又获得了200积分,更新其分数的命令为:
代码语言:bash
复制
    ZINCRBY game_rank:202410 200 player1
  • 返回值:返回更新后的分数。假设player1之前分数为1000,执行上述命令后,返回"1200"
  • 获取元素排名(ZRANK/ZREVRANK)ZRANK按分数从低到高返回元素排名(排名从0开始),ZREVRANK按分数从高到低返回排名。ZREVRANK game_rank:202410 player2
    • 命令格式ZRANK key memberZREVRANK key member
    • 示例:获取玩家player2在排行榜中的排名,从高到低排名命令如下:
    • 返回值:返回元素的排名,如果元素不存在则返回nil。比如player2game_rank:202410中排名第2(从高到低且排名从0开始),则返回(integer) 1 ;若player2不存在,返回(nil)
  • 获取指定排名范围的元素(ZRANGE/ZREVRANGE)ZRANGE按分数从低到高返回指定排名范围内的元素,ZREVRANGE按分数从高到低返回。可通过withscores参数返回元素及其分数。ZREVRANGE game_rank:202410 0 2 withscores
    • 命令格式ZRANGE key start end [withscores]ZREVRANGE key start end [withscores]
    • 示例:获取游戏排行榜中排名前三的玩家及其分数,命令如下:
    • 返回值:返回指定排名范围内的元素列表,如果使用withscores则同时返回分数。例如,若game_rank:202410中有player4(分数1800)、player3(分数1500)、player5(分数1200),执行上述命令后,返回值可能为:
代码语言:txt
复制
1) "player4"
2) "1800"
3) "player3"
4) "1500"
5) "player5"
6) "1200"
  1. 获取元素分数(ZSCORE):获取ZSet中指定元素的分数。ZSCORE game_rank:202410 player1
    • 命令格式ZSCORE key member
    • 示例:获取玩家player1的分数,命令为:
    • 返回值:返回元素的分数,如果元素不存在则返回nil。若player1分数为1200,返回"1200";若player1不存在于game_rank:202410中,返回(nil)
  2. 删除元素(ZREM):从ZSet中删除一个或多个元素。ZREM game_rank:202410 player2
    • 命令格式ZREM key member [member ...]
    • 示例:假设玩家player2退出游戏,要从排行榜中删除他,命令如下:
    • 返回值:返回成功删除的元素数量。若player2存在于game_rank:202410中,执行命令后返回(integer) 1;若player2不存在,则返回(integer) 0

综合示例:构建一个简单的游戏排行榜

假设我们要构建一个游戏排行榜,记录玩家的积分并实时更新和展示。

  1. 初始化排行榜:添加一些初始玩家数据。ZADD game_rank:202410 1500 player3 1800 player4 1200 player5执行该命令后,若game_rank:202410原本为空,返回(integer) 3。 2. 更新玩家积分:玩家player3获得了300积分。ZINCRBY game_rank:202410 300 player3执行该命令后,假设player3之前积分是1500,返回"1800"。 3. 获取排行榜信息 获取排行榜前两名玩家及其分数:
代码语言:bash
复制
ZREVRANGE game_rank:202410 0 1 withscores

假设此时game_rank:202410中有player4(分数1800)、player3(分数1800)、player5(分数1200),返回值可能为:

代码语言:txt
复制
1) "player4"
2) "1800"
3) "player3"
4) "1800"
  • 获取玩家player4的排名:
代码语言:txt
复制
ZREVRANK game_rank:202410 player4

player4game_rank:202410中排名第1(从高到低且排名从0开始),返回(integer) 0

  • 获取玩家player5的分数:
代码语言:bash
复制
    ZSCORE game_rank:202410 player5

player5分数为1200,返回"1200"

通过上述Redis的ZSet命令,我们可以高效地实现一个功能完善的排行榜系统,满足实时更新、查询排名和分数等需求。在实际应用中,可根据具体业务场景对这些命令进行灵活组合和扩展。

亿级用户排行榜:挑战与突破

分治思想:化繁为简的智慧策略

当面对亿级用户的超大规模排行榜时,传统的设计方案往往显得力不从心。以王者荣耀为例,为了应对如此庞大的用户基数,我们引入了分治思想。首先,根据游戏中的段位将用户进行分组,每个段位如同一个独立的“桶”,对应Redis中的一个key。通过这种方式,将庞大的用户群体进行了有效的划分,降低了单个排行榜的处理压力。

桶的管理与排名计算:精准定位的关键

在每个桶中,我们可以利用Redis的zcard命令快速获取桶内集合的大小,这一操作的时间复杂度仅为O(1),为后续的排名计算提供了高效的支持。当计算用户的全服排名时,需要综合考虑其在所在桶内的排名以及前面所有桶中的元素个数。例如,一个处于钻石段位的用户,其全服排名等于钻石段位桶内的排名加上前面青铜、白银、黄金等段位桶内的元素总数。

然而,在实际应用中,用户在各个段位的分布往往并不均匀。为了进一步优化排行榜的性能,我们需要对每个桶进行更为细致的划分,比如按照小段位或者积分区间进行细分。同时,借助数据分析手段,深入了解用户的分布规律,运用数学知识精准预估每个桶的大小,从而实现排行榜的高效管理与精准计算。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基于数据库:小场景下的实用之选
  • 基于Redis:高效排行榜的核心引擎
  • ZSet数据结构基础
    • 实现排行榜常用的Redis命令
  • 综合示例:构建一个简单的游戏排行榜
  • 亿级用户排行榜:挑战与突破
    • 分治思想:化繁为简的智慧策略
    • 桶的管理与排名计算:精准定位的关键
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档