🚦 Redis 行为限流方案设计:基于 IP、用户、设备 ID 的高效防刷实践

6 阅读3分钟

📖 模块简介

在金融业务、支付系统等高安全性场景下,防止恶意刷接口、撞库、爬虫等攻击至关重要。通过 Redis 的高性能计数器和灵活的数据结构,可以实现基于 IP、用户、设备 ID 的多维度行为限流,有效抵御各类高频攻击。本文介绍其原理、典型场景、Go 语言实现、常见问题与最佳实践。


🧠 基础原理

  1. 计数器限流:利用 Redis INCR/EXPIRE 组合,实现固定窗口限流(如每分钟最多 N 次)。
  2. 滑动窗口/漏桶/令牌桶:可用 Lua 脚本实现更精细的滑动窗口、漏桶、令牌桶等限流算法。
  3. 多维度限流:以 IP、用户 ID、设备 ID 等为 key 维度,分别计数,灵活组合防护。
  4. 原子性与高可用:所有操作在 Redis 端原子执行,适合高并发场景。

💼 金融业务应用场景

  • 登录/注册接口防刷:对单 IP、单用户、单设备多维限流,防止撞库和恶意注册。
  • 支付/提现频率控制:限制单用户、单设备在短时间内的高频操作,防止资金风险。
  • API 接口防爬虫:对外开放接口按 IP、用户、设备限流,保护系统稳定。
  • 营销活动防刷单:活动期间,防止同一用户或设备频繁参与。

💻 示例代码(Go + Redis)

1. 固定窗口限流(以 IP 为例,每分钟最多 10 次)

ip := "192.168.1.100"
key := fmt.Sprintf("limit:ip:%s:%d", ip, time.Now().Unix()/60) // 按分钟分片
limit := 10
cnt, err := rdb.Incr(ctx, key).Result()
if err != nil {
    // Redis 异常处理
}
if cnt == 1 {
    rdb.Expire(ctx, key, 61*time.Second) // 设置过期
}
if cnt > int64(limit) {
    // 超过限制,拒绝请求
    return errors.New("请求过于频繁,请稍后再试")
}
// 继续业务逻辑

2. 多维度限流(用户+设备)

userID := "u1001"
deviceID := "dev123"
key := fmt.Sprintf("limit:userdev:%s:%s:%d", userID, deviceID, time.Now().Unix()/60)
limit := 5
cnt, err := rdb.Incr(ctx, key).Result()
if cnt == 1 {
    rdb.Expire(ctx, key, 61*time.Second)
}
if cnt > int64(limit) {
    // 拒绝请求
}

🚨 常见问题与注意事项

  • 分布式部署一致性:所有业务节点需共用同一 Redis,防止限流绕过。
  • 高并发下的原子性:INCR/EXPIRE 需保证原子性,建议用 Lua 脚本封装。
  • 滑动窗口实现复杂:如需更平滑限流,需用 Lua 实现滑动窗口算法。
  • 误伤正常用户:阈值需结合业务实际,避免误伤。
  • key 膨胀与内存管理:大量限流 key 需合理设置过期,防止内存膨胀。

✅ 最佳实践建议

  1. 多维度(IP、用户、设备)组合限流,提升安全性。
  2. 阈值设置结合业务实际,动态调整。
  3. 高并发场景建议用 Lua 脚本保证原子性。
  4. 定期监控限流 key 数量与内存占用。
  5. 结合黑名单、验证码等多重防护措施。

📚 延伸阅读