哈希算法题目:设计一个基于哈希的分布式实时游戏排行榜系统(支持分数更新和Top K查询)
字数 1412 2025-11-27 07:17:44
哈希算法题目:设计一个基于哈希的分布式实时游戏排行榜系统(支持分数更新和Top K查询)
题目描述
设计一个分布式实时游戏排行榜系统,需要支持以下操作:
add_score(user_id, score):为指定用户添加分数(可重复调用,分数累加)get_top_k(k):返回当前排名前K的用户ID和总分get_user_rank(user_id):返回指定用户的排名(按总分降序,排名从1开始)
要求系统能处理高并发更新和查询,并保证数据一致性。
解题过程
步骤1:分析核心挑战
- 高并发更新:多个玩家可能同时提交分数,需保证分数更新的原子性。
- 实时排名计算:每次查询Top K或用户排名时,若全量排序所有用户,时间复杂度为O(NlogN),无法满足实时性。
- 分布式扩展:单机存储和计算可能成为瓶颈,需设计分片策略。
步骤2:选择数据结构
- 主数据结构:使用哈希表(
user_id -> 总分)存储用户分数,实现O(1)的分数更新。 - 排名优化:结合跳跃表(Skip List)或平衡树(如红黑树)维护分数排序:
- 跳跃表支持O(logN)的插入、删除和排名查询,且易于分布式扩展。
- 每个节点存储用户ID和总分,按分数降序排列(分数相同时按用户ID排序避免冲突)。
步骤3:分布式架构设计
- 分片策略:根据用户ID的哈希值分片(如
hash(user_id) % shard_num),将用户分数分布到多个节点。 - 全局排名计算:
- 每个分片维护本地Top K的跳跃表。
- 查询全局Top K时,使用堆合并算法:
- 从所有分片获取本地Top K(最大堆)。
- 用全局最大堆合并分片结果,每次弹出当前最高分用户,直到取满K个。
- 用户排名查询:
- 定位用户所在分片,获取其分数。
- 向所有分片查询分数高于该用户的用户数量,累加后+1即为全局排名。
步骤4:并发控制与一致性
- 分数更新:
- 使用分布式锁(如Redis原子操作)或CAS(Compare-and-Set)保证同一用户的分数更新原子性。
- 示例:
redis.INCRBY(user_id, score)实现原子累加。
- 排名数据同步:
- 异步更新跳跃表:分数更新后,通过消息队列触发跳跃表的插入/删除操作,避免阻塞写请求。
- 最终一致性:排名数据可能延迟几毫秒,但保证最终正确。
步骤5:详细操作流程
add_score实现:- 根据
user_id哈希定位分片。 - 原子操作更新哈希表中的分数(如
atomic_add)。 - 发送事件到消息队列,触发跳跃表更新(如删除旧分数节点,插入新分数节点)。
- 根据
get_top_k实现:- 向所有分片请求本地Top K(通过跳跃表O(K)获取)。
- 用最大堆合并结果:
heap = MaxHeap() for shard in shards: heap.push(shard.top_k) # 每个分片的Top K列表 result = [] while k > 0 and heap not empty: user = heap.pop() result.append(user) k -= 1
get_user_rank实现:- 定位用户分片,获取其分数
S。 - 向所有分片查询分数严格大于
S的用户数量(通过跳跃表O(logN)范围统计)。 - 全局排名 = 大于
S的用户数 + 1。
- 定位用户分片,获取其分数
步骤6:优化与扩展
- 缓存Top K结果:对频繁查询的K值(如K=10/100),缓存排名结果,短期内的分数更新只增量更新缓存。
- 时间窗口排名:扩展为支持近24小时排名时,可结合时序数据库存储分数变化,按时间聚合查询。
总结
本题通过哈希表实现快速分数更新,跳跃表维护有序排名,分片策略解决扩展性,最终实现高效的分布式排行榜系统。