Redis操作慢90%以上源于客户端,需用AOP拦截RedisTemplate.execute()分三段计时(连接获取、序列化、命令执行),按命令类型分级设阈值(如GET/SET为20ms、SCAN为500ms),并针对Lettuce异步特性调用future.get()精准测时。

Redis操作执行慢,90%以上不是Redis服务端问题,而是客户端连接、序列化或命令执行阶段卡在Spring Boot进程内。直接看redis-cli --latency没用,得进应用里测真实耗时。
为什么RedisTemplate.execute()拦截最准
所有opsForValue().get()、boundValueOps().set()最终都调用execute()或executePipelined(),拦这里覆盖全、无遗漏。别拦executeWithStickyConnection()——集群模式才用,普通项目基本不涉及。
- 切面表达式用
@Around("execution(* org.springframework.data.redis.core.RedisTemplate.execute*(..))"),同时覆盖execute、executePipelined、executeReadOnly - 排除
getConnection()和closeConnection():它们不发Redis命令,拦了会把连接池等待时间误算成“Redis执行慢” - 日志里必须记录
method.getName()+args[0](即RedisCallback内容),否则看不出是不是SCAN这类天然慢操作
怎么分三段计时才不漏关键瓶颈
AOP拦execute()只测到“命令发出→响应返回”这段,但慢可能卡在前面:拿连接、序列化、甚至Lettuce异步提交任务本身。必须用StopWatch拆成三段:
- ① 连接获取耗时:从
connectionFactory.getConnection()前开始,到拿到RedisConnection后结束 → 高说明lettuce.pool.max-idle太小或连接泄漏 - ② 序列化耗时:从
serializer.serialize()开始,到execute()调用前结束 → 高大概率是用了JdkSerializationRedisSerializer,换GenericJackson2JsonRedisSerializer - ③ 命令执行耗时:
execute()返回后,到反序列化完成 → 这才是纯Redis命令+网络往返时间
如何设合理阈值避免日志刷爆
固定阈值(如>100ms)会误报:SCAN扫百万key就是慢,但合理;GET耗50ms就可疑。必须按命令类型分级:
-
GET/SET/DEL类单key命令:阈值设20ms -
HGETALL/LRANGE类批量命令:阈值设100ms -
SCAN/SUNION类扫描/聚合命令:阈值设500ms,且日志加isScan:true标记 - 超时日志只打
ERROR级,且必须带if (duration > threshold)条件判断,别用WARN
最容易被忽略的是Lettuce默认异步行为:execute()返回的是Future,AOP拦到的只是“提交任务”的耗时,不是真正执行完的时间。要测准,得在切面里调future.get(timeout, TimeUnit),但这会阻塞线程——所以生产环境建议只对慢日志采样启用,别全量开。


















