redistemplate执行慢的根因在客户端,需用aop拦截execute()等方法分段计时,聚焦连接获取、序列化、命令执行三阶段耗时,并按命令类型分级设阈值(如get/set为20ms、scan为500ms),避免误报。

为什么RedisTemplate执行慢但没报错
Spring Boot里用 RedisTemplate 调 Redis,偶尔卡顿几秒,redis-cli --latency 测服务端又正常,说明问题大概率出在客户端:连接池阻塞、序列化耗时、命令堆积或网络抖动。单纯靠 @EventListener 监听 RedisConnectionFailureException 捕不到“慢”,得主动测耗时。
用AOP拦截RedisTemplate的execute方法最准
RedisTemplate 所有读写最终都走 execute() 或 executePipelined(),比拦截 opsForValue().get() 这类封装方法更底层、覆盖更全。注意别拦 executeWithStickyConnection()(集群模式专用,一般用不到)。
实操建议:
- 切面表达式用
@Around("execution(* org.springframework.data.redis.core.RedisTemplate.execute*(..))"),覆盖execute、executePipelined、executeReadOnly - 排除掉
getConnection()和closeConnection()—— 它们不发命令,拦了反而干扰判断 - 日志里必须记清
method.getName()+args[0](即RedisCallback的实际逻辑,比如是否含scan这种游标操作)
怎么定义“慢”并避免日志刷爆
直接设固定阈值(比如 >100ms)容易误报:SCAN 扫百万 key 就是慢,但属于合理慢;而 GET 耗 50ms 就可疑。所以得结合命令类型分级:
-
GET/SET/DEL类单key命令:阈值设 20ms -
HGETALL/LRANGE类批量命令:阈值设 100ms -
SCAN/SUNION类扫描/聚合命令:阈值设 500ms,并额外打上isScan:true标签 - 超时日志只打 ERROR 级,且加
if (duration > threshold) { log.error(...) },别用 WARN 防止淹没有效信息
别漏掉连接获取和序列化这两块耗时
AOP 拦 execute() 只能测“命令执行+响应解析”阶段,但慢可能来自前面两步:从连接池拿连接(pool.getResource())、Java 对象序列化成字节数组(RedisTemplate.getValueSerializer().serialize())。这两块得单独埋点:
- 在切面里用
StopWatch分三段计时:① 获取连接前到拿到连接后;② 序列化开始到execute()调用前;③execute()返回后到反序列化完成 - 如果①耗时高,查
lettuce.pool.max-idle和min-idle是否过小;如果②耗时高,换GenericJackson2JsonRedisSerializer替掉默认的JdkSerializationRedisSerializer - 注意:Lettuce 默认异步,
execute()返回的是Future,AOP 拦到的是提交任务的耗时,不是真正执行完的耗时——得用future.get(timeout, TimeUnit)才准,但会阻塞,慎用
真实慢查往往卡在序列化或连接池争抢,而不是 Redis 本身。打日志时把这三段耗时分开打,比只记一个总时间有用得多。










