MST
星途 面试题库

面试题:缓存设计:Redis Sorted Set 排行榜的复杂功能实现

现在要求在排行榜功能中增加如下特性:1. 支持按照不同游戏模式分别统计排行榜;2. 能够快速查询某个玩家在其所属分组(例如按照等级划分的分组)中的排名。基于 Redis Sorted Set 设计一套完整的方案,包括数据结构设计、操作逻辑以及如何应对高并发场景下的数据一致性问题。
31.7万 热度难度
后端开发缓存设计

知识考点

AI 面试

面试题答案

一键面试

数据结构设计

  1. 按照游戏模式存储排行榜
    • 对于每个游戏模式,使用一个 Redis Sorted Set 来存储玩家的排行榜信息。例如,假设游戏模式有 game_mode_1game_mode_2 等,Sorted Set 的 key 可以设计为 leaderboard:game_mode_1leaderboard:game_mode_2 等。
    • Sorted Set 中的 member 为玩家 ID,score 为玩家在该游戏模式下的得分。这样通过 Sorted Set 的有序特性,可以很方便地获取玩家的排名。
  2. 按分组存储玩家排名
    • 对于按照等级划分的分组(假设等级有 level_1level_2 等),为每个分组创建一个 Sorted Set。例如,leaderboard:level_1:game_mode_1leaderboard:level_1:game_mode_2 等。同样,member 为玩家 ID,score 为玩家在对应游戏模式下的得分。

操作逻辑

  1. 更新排行榜
    • 当玩家在某个游戏模式下有新的得分时,使用 ZADD 命令更新对应的 Sorted Set。例如,对于 game_mode_1,如果玩家 player_1 有新得分 new_score,执行 ZADD leaderboard:game_mode_1 new_score player_1
    • 同时,要更新该玩家所属分组的 Sorted Set。假设 player_1 属于 level_1 分组,执行 ZADD leaderboard:level_1:game_mode_1 new_score player_1
  2. 查询玩家排名
    • 查询玩家在某个游戏模式下的总排名,使用 ZRANK 命令。例如,查询 player_1game_mode_1 中的排名,执行 ZRANK leaderboard:game_mode_1 player_1
    • 查询玩家在其所属分组中的排名,同样使用 ZRANK 命令。例如,查询 player_1level_1 分组的 game_mode_1 中的排名,执行 ZRANK leaderboard:level_1:game_mode_1 player_1

应对高并发场景下的数据一致性问题

  1. 使用 Redis 事务
    • 在更新排行榜时,可以使用 Redis 的 MULTI 和 EXEC 命令组成事务。例如:
    MULTI
    ZADD leaderboard:game_mode_1 new_score player_1
    ZADD leaderboard:level_1:game_mode_1 new_score player_1
    EXEC
    
    • 这样可以保证这两个操作要么都执行成功,要么都不执行,避免部分更新导致的数据不一致。
  2. 乐观锁机制
    • 在更新数据前,先获取当前玩家的得分,例如使用 ZSCORE 命令获取 player_1leaderboard:game_mode_1 中的得分 old_score
    • 在更新时,通过 WATCH 命令监控该 Sorted Set。例如:
    WATCH leaderboard:game_mode_1
    MULTI
    ZADD leaderboard:game_mode_1 new_score player_1
    ZADD leaderboard:level_1:game_mode_1 new_score player_1
    EXEC
    
    • 如果在 WATCH 之后,leaderboard:game_mode_1 被其他客户端修改,EXEC 命令会返回 nil,表示事务执行失败,此时需要重新获取数据并再次尝试更新。
  3. 读写分离与缓存
    • 对于读操作,可以采用读写分离的策略,使用多个从节点来分担读压力。对于频繁查询的排名信息,可以在应用层设置本地缓存,减少对 Redis 的直接查询次数。例如,使用 Memcached 或者应用内的内存缓存(如 Python 的 functools.lru_cache 等)来缓存玩家排名。这样在高并发读场景下,可以快速从缓存中获取数据,提高响应速度。同时,需要注意缓存的更新策略,当排行榜数据发生变化时,及时更新缓存数据,以保证数据的一致性。