1. 基于Java集合的简单实现(适合小规模数据)
核心思路:使用
List
或Map
存储数据,通过排序算法维护排行榜。实现步骤:
- 定义数据结构:创建
Ranking
类管理排行榜,内部用List<Score>
存储数据。每个Score
对象包含用户ID、分数等字段,并实现Comparable
接口以支持排序。 - 添加分数:每次插入新数据时,调用
Collections.sort()
对列表进行降序排序。若列表长度超过最大限制(如TOP 100),移除末尾元素。 - 处理并列排名:若需按分数和时间排序,可在
Score
类中自定义比较逻辑(如分数相同则时间较早者排名更高)。 - 展示排行榜:遍历列表输出排名,或通过分页接口返回数据。
- 定义数据结构:创建
代码示例:
// 按分数降序、时间升序排序
students.sort(Comparator.comparing(Student::getScore).reversed()
.thenComparing(Student::getAge));
- 优缺点:
- 优点:实现简单,无需依赖外部组件。
- 缺点:数据量大时性能低(每次全量排序),且无法持久化。
2. 基于Redis的有序集合(适合高并发实时场景)
核心思路:利用Redis的
zset
数据结构,天然支持按分数排序和排名查询。实现步骤:
- 添加/更新分数:使用
zadd
命令插入用户的分数,Redis自动维护排序。 - 获取排名:
- 添加/更新分数:使用
用户当前排名:
zrevrank
(降序排名,从0开始)。分页查询:
zrevrangeWithScores
获取指定区间的用户及分数。
3. 处理相同分数:Redis默认按字典序排序,若需按时间排序,可将时间戳作为分数的小数部分(如score = 用户分数 + (1 - 时间戳/最大值)
)。代码示例:
// 分页获取排行榜(Jedis示例)
Set<Tuple> rankTuple = jedis.zrevrangeWithScores("rankKey", start, end);
- 优缺点:
- 优点:性能极高,支持高并发;天然支持分布式场景。
- 缺点:需维护Redis服务;复杂业务逻辑(如多维度排序)需额外设计。
3. 基于数据库的SQL实现(适合数据持久化场景)
核心思路:通过数据库查询排序,结合Java代码处理排名逻辑。
实现步骤:
- 数据存储:数据库表包含用户ID、分数、时间等字段。
- SQL排序:使用
ORDER BY
对分数降序排序,时间作为次要条件。 - Java处理排名:
遍历查询结果,动态计算排名(处理并列情况)。
若分数相同,排名不递增,例如:
100分(第1名), 90分(第2名), 90分(第2名)
。代码示例:
-- 查询排序结果(MySQL示例)
SELECT uid, score,
@rank := IF(@prev_score = score, @rank, @rank + 1) AS rank,
@prev_score := score
FROM user, (SELECT @rank := 0, @prev_score := NULL) vars
ORDER BY score DESC, create_time ASC;
- 优缺点:
- 优点:数据持久化,适合复杂查询。
- 缺点:大数据量时性能较差;需处理分页和排名逻辑。
4. 其他优化与进阶方案
- 分库分表:当数据量极大时(如亿级用户),按用户ID哈希分片存储。
- 冷热数据分离:将历史数据归档,仅对近期活跃数据排序。
- 定时任务更新:若实时性要求低,可定时计算排行榜并缓存结果。
面试回答要点总结
- 根据场景选择技术栈:
- 小数据量、简单需求 → Java集合。
- 高并发实时场景 → Redis。
- 持久化复杂查询 → 数据库 + Java逻辑。
- 处理并列排名:通过多字段排序(分数、时间等)或自定义排名算法。
- 性能优化:分页查询、异步更新、缓存机制等。
- 扩展性:分布式架构、数据分片等设计思路。
通过结合具体业务需求(如实时性、数据规模、持久化要求),选择最合适的实现方案,并展示对技术选型的深入理解。