如何设计一个实时排行榜系统?
排行榜是很常见的需求,今天聊聊怎么设计。
需求分析
典型的排行榜需求:
- 用户有分数
- 按分数排名
- 能查询排名
- 分数会实时变化
比如游戏积分榜、文章热度榜、销量排行榜。
方案一:数据库排序
最简单的方案,直接查数据库:
SELECT user_id, score
FROM user_score
ORDER BY score DESC
LIMIT 100
优点:简单
缺点:数据量大的时候很慢,而且每次都要全表排序
适用场景:数据量小、访问量低
方案二:Redis ZSET
Redis的有序集合天生适合做排行榜。
# 设置分数
ZADD ranking 100 user1
ZADD ranking 200 user2
ZADD ranking 150 user3
# 获取排行榜(分数高的在前)
ZREVRANGE ranking 0 99 WITHSCORES
# 查询某用户排名
ZREVRANK ranking user1
# 更新分数
ZINCRBY ranking 10 user1
优点:
- 性能好,O(logN)的时间复杂度
- 支持实时更新
- 原子操作,不用担心并发问题
缺点:
- 数据量特别大时(亿级)内存压力大
适用场景:大部分排行榜需求
方案三:分段+汇总
数据量特别大时,可以分段存储:
- 按分数段分成多个ZSET
- 查询时合并结果
或者用定时任务批量计算排名,存到缓存里。
设计要点
1. 考虑数据量
数据量小用MySQL就行,数据量大用Redis。
2. 考虑更新频率
更新频繁用Redis,更新不频繁可以定时批量计算。
3. 考虑排名方式
只要Top N还是要查询任意用户的排名?要求不同,方案不同。
4. 考虑并发
分数更新可能并发,用Redis的原子操作可以避免问题。
一个实际例子
假设做一个游戏积分榜:
- 10万用户
- 分数实时变化
- 需要展示Top 100
- 需要查询用户自己的排名
方案:
用Redis ZSET:
- key:
game:ranking - value:用户ID
- score:积分
用户积分变化时更新Redis,页面展示时直接从Redis获取。
这个方案能支撑百万级QPS,对于大部分场景足够了。
最后
排行榜是Redis的经典应用场景。
ZSET这个数据结构就是为排行榜而生的,用好了能解决大部分排行榜需求。
