面试题答案
一键面试数据结构设计
- 用户限流数据结构:
- 使用Redis的哈希(Hash)结构来存储不同用户类型的限流规则。例如,以
user_type_limit
为键,哈希表的字段为用户类型(如normal_user
,vip_user
,admin
),字段值为该用户类型的限流阈值(如每分钟允许的操作次数)。 - 示例:
- 使用Redis的哈希(Hash)结构来存储不同用户类型的限流规则。例如,以
HSET user_type_limit normal_user 100
HSET user_type_limit vip_user 500
HSET user_type_limit admin 1000
- 子操作限流数据结构:
- 同样使用哈希结构,以
sub_op_limit
为键,字段为子操作名称,字段值为该子操作的限流阈值。 - 示例:
- 同样使用哈希结构,以
HSET sub_op_limit sub_op1 50
HSET sub_op_limit sub_op2 30
- 用户操作计数数据结构:
- 对于每个用户的操作计数,使用Redis的字符串(String)结构。键的命名可以采用
user_{user_id}_{operation}_{sub_op}
的格式,值为该用户在当前窗口内执行该操作及子操作的次数。 - 例如,对于用户ID为123,操作
main_op
下的子操作sub_op1
,键为user_123_main_op_sub_op1
,初始值为0。
- 对于每个用户的操作计数,使用Redis的字符串(String)结构。键的命名可以采用
代码实现思路
- 获取限流规则:
- 从
user_type_limit
哈希表中根据用户类型获取限流阈值。 - 从
sub_op_limit
哈希表中根据子操作名称获取子操作的限流阈值。
- 从
- 检查限流:
- 每次用户执行操作时,先检查当前用户类型和子操作的限流阈值。
- 使用
INCR
命令对user_{user_id}_{operation}_{sub_op}
键进行自增操作,获取当前操作次数。 - 如果当前操作次数超过了对应的限流阈值,则拒绝该操作;否则允许操作继续。
- 窗口管理:
- 可以通过设置键的过期时间来实现固定窗口。例如,在每次设置
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
可能遇到的问题和应对策略
- 并发问题:
- 问题:在高并发场景下,多个请求同时对计数键进行自增操作可能导致不准确的计数。
- 策略:Redis的
INCR
命令是原子操作,天然保证了并发安全,无需额外处理。
- 窗口边界问题:
- 问题:在窗口切换瞬间,可能会出现短时间内请求数超过预期的情况(如在窗口结束前最后一秒大量请求,窗口切换后又可以继续请求)。
- 策略:可以采用滑动窗口算法替代固定窗口算法,但如果必须使用固定窗口,可以适当增加限流阈值以应对这种短暂的突发情况。
- Redis故障问题:
- 问题:如果Redis出现故障,限流功能将无法正常工作。
- 策略:采用Redis集群模式,增加系统的可用性。同时,可以在应用层设置本地缓存(如
lru_cache
),在Redis故障时提供有限的限流功能作为临时解决方案。