Redis QPS上不去主因是限流设计不当:高频INCR/EXPIRE等命令、无pipeline、Lua脚本阻塞、连接池配置不合理,导致单线程瓶颈;Redisson通过预取令牌机制将多次请求合并,显著降低Redis访问频次,提升QPS。

Redis 的 QPS 上不去,不是 Redis 本身“跑不快”,而是限流逻辑把 Redis 变成了瓶颈——你用它做限流,却没让它扛住限流的流量。
为什么限流反而压垮 Redis
限流请求本身要频繁访问 Redis:每进一个请求,就要 INCR、EXPIRE、GET 或执行 Lua 脚本。如果单机 QPS 5000,集群 20 台服务,每秒就是 10 万次 Redis 命令。但默认配置下,Redis 单实例吞吐远达不到这个量级,尤其当命令带网络往返、序列化、连接争抢时。
- Redis 是单线程处理命令,但网络 IO 和连接管理由多线程(Lettuce)或阻塞 IO(Jedis)承担,这部分容易成为隐形瓶颈
- 每次限流调用都走一次完整 TCP 往返,没 pipeline、没批量,延迟直接翻倍
- Lua 脚本虽原子,但执行时间过长会阻塞主线程;若脚本里有
redis.call("HGETALL")这类 O(N) 操作,QPS 瞬间腰斩 - 客户端连接池没配好,
max-active太小导致线程排队等待连接,CPU 在空转而不是发命令
Redisson RRateLimiter 为什么比手写 Lua 更稳
Redisson 的 RRateLimiter 不是每次请求都打 Redis,它用「预取令牌」机制降低频次:客户端一次性从 Redis 拿一批令牌(比如 10 个),本地消耗完再补。这把 1000 次 Redis 请求压缩成 100 次,QPS 自然上得去。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 它底层用的是
PTTL+PEXPIRE+INCRBY组合,避免 Lua 脚本锁住整个 Redis - 支持自动重试和失败降级,不会因为一次 Redis 超时就让整个接口拒绝服务
- 与 Spring Boot3 的自动装配兼容,
@Bean注入后直接tryAcquire(),不用自己拼 key、算窗口 - 注意:必须配
redisson.singleServerConfig.connectionMinimumIdleSize≥ 10,否则预取失败率高
连接池参数怎么设才不拖后腿
连接池不是越大越好,也不是越小越省资源——它得匹配你的实际限流 QPS 和 Redis 处理能力。
-
max-active推荐设为「预估峰值限流 QPS × 1.2~1.5」,例如限流阈值 8000 QPS,那就设 10000;设成 20000 反而引发 Netty 线程争抢和 GC 压力 -
max-wait必须 ≥ 2000ms,太短会导致线程快速失败→重试→再失败,形成 CPU 飙升的“重试风暴” - 禁用
testOnBorrow(Jedis)或validateConnection(Lettuce),限流路径上不能加任何额外 IO 判断 - 用 Actuator 的
redisendpoint 监控active/idle比值,持续 > 0.9 说明连接不够,
真正卡 QPS 的往往不是 Redis,而是你的 key 设计
限流 key 如果带高基数字段(比如用户手机号、订单 ID),会导致 Redis 内存碎片+哈希槽不均+淘汰压力大,最终表现为命令延迟上升、QPS 波动剧烈。
- 避免用
"limit:uid:" + userId这种 key,改用聚合维度:比如按接口路径限流"limit:api:/order/create",或按 IP 段限流"limit:ip:192.168.1.*" - 不要给限流 key 设永不过期,必须用
EX或PX显式控制生命周期,否则内存只增不减 - 如果业务允许,把滑动窗口拆成多个固定窗口(如 100ms 一格),用
INCR+EXPIRE替代复杂 Lua,性能提升明显 - 查慢日志:
redis-cli --latency -h xxx看 P99 延迟,> 5ms 就得查网络或 Redis 本体负载
限流不是往 Redis 上堆请求,而是让 Redis 少干活、干得准、别卡住。关键不在算法多炫,而在每个 Redis 命令是否必要、是否可省、是否可批。

















