MST

星途 面试题库

面试题:Redis固定窗口限流与复杂业务需求深度适配

现有一个复杂业务场景,不同类型用户(普通用户、VIP用户、管理员等)对同一功能有不同的限流规则,且该功能涉及多个子操作,每个子操作也有各自的限流要求。同时,系统需要保证在高并发下的限流准确性和性能。请详细设计一个基于Redis固定窗口限流的整体解决方案,包括数据结构设计、代码实现思路以及可能遇到的问题和应对策略。
47.5万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

数据结构设计

  1. 用户限流数据结构
    • 使用Redis的哈希(Hash)结构来存储不同用户类型的限流规则。例如,以user_type_limit为键,哈希表的字段为用户类型(如normal_uservip_useradmin),字段值为该用户类型的限流阈值(如每分钟允许的操作次数)。
    • 示例:
HSET user_type_limit normal_user 100
HSET user_type_limit vip_user 500
HSET user_type_limit admin 1000
  1. 子操作限流数据结构
    • 同样使用哈希结构,以sub_op_limit为键,字段为子操作名称,字段值为该子操作的限流阈值。
    • 示例:
HSET sub_op_limit sub_op1 50
HSET sub_op_limit sub_op2 30
  1. 用户操作计数数据结构
    • 对于每个用户的操作计数,使用Redis的字符串(String)结构。键的命名可以采用user_{user_id}_{operation}_{sub_op}的格式,值为该用户在当前窗口内执行该操作及子操作的次数。
    • 例如,对于用户ID为123,操作main_op下的子操作sub_op1,键为user_123_main_op_sub_op1,初始值为0。

代码实现思路

  1. 获取限流规则
    • user_type_limit哈希表中根据用户类型获取限流阈值。
    • sub_op_limit哈希表中根据子操作名称获取子操作的限流阈值。
  2. 检查限流
    • 每次用户执行操作时,先检查当前用户类型和子操作的限流阈值。
    • 使用INCR命令对user_{user_id}_{operation}_{sub_op}键进行自增操作,获取当前操作次数。
    • 如果当前操作次数超过了对应的限流阈值,则拒绝该操作;否则允许操作继续。
  3. 窗口管理
    • 可以通过设置键的过期时间来实现固定窗口。例如,在每次设置user_{user_id}_{operation}_{sub_op}键时,设置过期时间为窗口时间(如1分钟)。过期后,该键自动删除,下一个窗口重新计数。

以下是Python示例代码(使用redis - py库):

import redis

class RateLimiter:
    def __init__(self, host='localhost', port=6379, db=0):
        self.redis_client = redis.StrictRedis(host=host, port=port, db=db)

    def get_user_type_limit(self, user_type):
        return int(self.redis_client.hget('user_type_limit', user_type))

    def get_sub_op_limit(self, sub_op):
        return int(self.redis_client.hget('sub_op_limit', sub_op))

    def is_allowed(self, user_id, user_type, operation, sub_op):
        user_type_limit = self.get_user_type_limit(user_type)
        sub_op_limit = self.get_sub_op_limit(sub_op)

        key = f'user_{user_id}_{operation}_{sub_op}'
        current_count = self.redis_client.incr(key)

        if current_count == 1:
            self.redis_client.expire(key, 60)  # 设置1分钟过期

        if current_count > user_type_limit or current_count > sub_op_limit:
            return False
        return True

可能遇到的问题和应对策略

  1. 并发问题
    • 问题:在高并发场景下,多个请求同时对计数键进行自增操作可能导致不准确的计数。
    • 策略:Redis的INCR命令是原子操作,天然保证了并发安全,无需额外处理。
  2. 窗口边界问题
    • 问题:在窗口切换瞬间,可能会出现短时间内请求数超过预期的情况(如在窗口结束前最后一秒大量请求,窗口切换后又可以继续请求)。
    • 策略:可以采用滑动窗口算法替代固定窗口算法,但如果必须使用固定窗口,可以适当增加限流阈值以应对这种短暂的突发情况。
  3. Redis故障问题
    • 问题:如果Redis出现故障,限流功能将无法正常工作。
    • 策略:采用Redis集群模式,增加系统的可用性。同时,可以在应用层设置本地缓存(如lru_cache),在Redis故障时提供有限的限流功能作为临时解决方案。