Skip to content
进阶

一句话答案

频繁增删导致 jemalloc 内存碎片,mem_fragmentation_ratio > 1.5 需关注,Redis 4.0+ 支持自动碎片整理。

核心要点

四种限流算法对比:

1. 固定窗口(Fixed Window)

每个固定时间窗口(如每分钟)统计请求数,超过阈值拒绝

Redis 实现:
  key = "rate_limit:{userId}:{minute}"
  INCR key                     # 计数 +1
  EXPIRE key 60                # 60s 后窗口过期

缺点:窗口边界问题:
  0:59 发 100 个请求,1:01 发 100 个请求
  这 2s 内共 200 个请求(远超限流阈值),但两个窗口各 100 个,都不超限

2. 滑动窗口(Sliding Window)

以当前时间为右边界,往前推时间窗口,更准确

Redis 实现(ZSet):
  key = "rate_limit:{userId}"
  score = 当前时间戳(毫秒)
  member = requestId(唯一)
  
  ZADD key currentTime requestId        # 记录请求
  ZREMRANGEBYSCORE key 0 (currentTime - windowSize)  # 删除窗口外的请求
  count = ZCARD key                      # 统计窗口内的请求数
  if count > limit: 拒绝请求

优点:无窗口边界问题,精确
缺点:内存占用较高(需存储每次请求)

3. 漏桶(Leaky Bucket)

请求进入桶,桶以固定速率漏水(处理请求)
桶满则溢出(拒绝新请求)

特点:强制平滑输出(固定速率处理)
适用:有严格速率要求的输出(如消息发送、API 调用)
缺点:无法应对突发流量(固定速率,无法加速)

4. 令牌桶(Token Bucket)

令牌桶以固定速率生产令牌
每个请求消耗一个令牌,没有令牌则拒绝
桶有容量上限(支持短时突发)

特点:允许突发流量(桶里有积累的令牌)
适用:允许一定突发的 API 限流
实现:Guava RateLimiter、Redisson RRateLimiter

Redis 实现(Token Bucket):
  lua 脚本原子执行:
  local tokens = redis.call('get', key) or capacity
  local now = tonumber(ARGV[1])
  local lastTime = redis.call('get', key..':time') or now
  
  -- 根据时间差补充令牌
  local newTokens = tokens + (now - lastTime) * rate
  newTokens = math.min(newTokens, capacity)
  
  if newTokens >= 1 then
      redis.call('set', key, newTokens - 1)
      return 1  -- 允许
  else
      return 0  -- 拒绝

实际生产使用 Lua 脚本保证原子性:

lua
-- 固定窗口限流(Lua,原子执行)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('incr', key))
if current == 1 then
    redis.call('expire', key, ARGV[2])  -- 第一次设置过期时间
end
if current > limit then
    return 0  -- 超出限制
else
    return 1  -- 允许通过
end

文档完

复习建议:

  1. 缓存三件套(Q10)是必背题,穿透/击穿/雪崩的定义、场景、解决方案要对应清晰,不能混淆
  2. Redisson 加锁流程(Q19)中 Lua 脚本的逻辑要能口述:先判断是否存在 → 不存在则 hset + pexpire → 存在且是自己则 hincrby(重入)→ 否则返回剩余 TTL
  3. 缓存一致性(Q22)要说清楚"先更新DB再删除Cache"的原因,以及为什么是"删除"而不是"更新",这是面试官最爱追问的点
  4. ZSet 底层(Q3)一定要说"skiplist + hashtable 双结构",一个负责有序查询,一个负责 O(1) 查 score,说出两个原因加分明显
  5. 集群架构(Q25-Q31)要能说清三种部署模式的选型决策,Sentinel 的 SDOWN/ODOWN 两阶段判断,Cluster 的 16384 slot + MOVED/ASK 两种重定向的区别
追问与易错

追问方向:

  • 碎片率太低说明什么?
  • 自动碎片整理影响?
  • 什么操作容易导致碎片?

易错点:

  • ❌ 碎片率高就有问题——1.0-1.5 正常
  • ❌ 重启是唯一方案——4.0+ 有自动整理

💡 记忆锚点

内存碎片 = 停车场空位:频繁进出导致车位东一个西一个(碎片),明明有空位却停不下大车。碎片率(mem_fragmentation_ratio)超1.5就该整理车位了,Redis 4.0+自带代客泊车服务(自动碎片整理)。