本专题「容错四大金刚 - 超时」连载顺序:
Redis 作为分布式缓存、分布式锁核心组件,线上线程阻塞、数据脏写、死锁故障大多源于不合理超时、不了解底层重试黑白名单、锁编码不规范。本文结合团队线上固化基线配置、Redisson 源码、真实故障复盘,分层拆解 TCP 连接、命令执行、分布式锁三层超时体系,输出架构评审约束标准与生产编码规范。
线上 Redis 三类高频稳定性故障:
团队已固化一套经过大促压测的 Redisson 基线,所有业务修改参数必须提交架构评审,禁止私自调整。很多开发不清楚 Redisson 底层自动重试黑白名单规则,极易埋下数据风险,本文完整拆解底层判定逻辑、边界风险、标准化落地方案。
自上而下优先级:分布式锁业务层 > 命令执行层 > TCP 连接池层,三层层层兜底,任意一层触发超时立刻终止请求、释放业务线程。
表格
分层 | 管控对象 | 核心配置 | 触发后果 |
|---|---|---|---|
分布式锁业务层 | 加锁等待时长、锁自动过期 | waitTime、leaseTime、lockWatchdogTimeout | 等待超时拿锁失败;未指定 leaseTime 自动 30s 看门狗续期,防止死锁 |
命令执行层 | 单条 Redis 读写最大耗时 | timeout=200ms | 抛出RedisResponseTimeoutException,按黑白名单判定是否重试 |
TCP 连接池层 | TCP 握手、空闲连接回收 | connectTimeout、idleConnectionTimeout | 建连失败销毁连接,闲置连接定时清理,避免僵死通道 |
网关总超时 > 应用接口总耗时 > RPC 调用超时 > Redisson 命令 timeout (200ms) > Redisson 建连 connectTimeout (3000ms
核心约束:Redis 单命令严格限制 200ms,防止慢请求长期占用业务线程池。
spring:
redis:
redisson:
config: |
singleServerConfig:
# 空闲连接回收:仅空闲连接超过16个时,闲置15min自动关闭,减少TCP频繁创建销毁
idleConnectionTimeout: 900000
# TCP三次握手建连超时,仅新建连接生效
connectTimeout: 3000
# 单命令往返最大耗时(核心红线200ms)
timeout: 200
# 幂等安全命令最大自动重试次数
retryAttempts: 3
# 每次重试间隔200ms,适配毫秒级缓存场景
retryInterval: 200
# 最小空闲连接,流量洪峰无需频繁新建连接
connectionMinimumIdleSize: 16
# 单Pod最大连接,防止打满Redis maxclients
connectionPoolSize: 64
# 分布式锁看门狗默认过期时长
lockWatchdogTimeout: 30000idleConnectionTimeout: 900000(15min)
仅当空闲连接数量大于 16 才执行回收;设置较长时长是为提升长连接复用率,降低网络开销。connectTimeout: 3000(3s)
专门处理跨机房、瞬时网络抖动的 TCP 握手等待,和 200ms 命令响应超时完全隔离。timeout: 200(核心强制红线)
所有常规读写必须 200ms 内完成往返;最坏 3 次重试总耗时:200 + 3 * 200 = 800ms,低于接口标准 1s 上限。retryAttempts: 3 / retryInterval: 200
仅白名单幂等命令自动重试,短间隔快速自愈瞬时网络闪断。protected boolean isResendAllowed(int attempt, int attempts) {
return attempt < attempts
&& !noRetry
&& (command == null || (!command.isBlockingCommand() && !command.isNoRetry()));
}同时满足 4 个条件才会自动重试:
NO_RETRY非幂等黑名单内。纯读:GET、HGET、HMGET、LRANGE、SMEMBERS
幂等写:SET、HSET、DEL、EXPIRE、PEXPIRE
正常网络超时场景,HINCRBY 会被isNoRetry()拦截,底层不会发起重试;
但存在极端半开网络边界场景:
风险根源不是 Redisson 自动重试,而是业务层捕获超时无幂等兜底、自行重试,因此计数、库存类操作必须业务层增加唯一流水幂等标识。
waitTime:获取锁最大等待时长,线上统一推荐 1000ms,避免大量线程排队阻塞;leaseTime:锁持有过期时间,不手动指定自动开启 30s 看门狗,每 10s 续期。// 隐患写法:无waitTime、无leaseTime,unlock Redis超时会残留锁30s
public void badOrderLock() {
RLock lock = redissonClient.getLock("order_lock_10086");
try {
lock.lock();
// Redis读写触发200ms超时抛出异常,直接跳出finally
} finally {
// unlock底层Redis操作同样会触发200ms超时,解锁失败
lock.unlock();
}
}故障现象:Redis 瞬时抖动,解锁命令超时未释放锁,所有下单请求阻塞 30s。
// 框架封装注解,内置30s默认leaseTime兜底
@RedisLock(source = "order", leaseTime = 30000)
private RedissonLock redissonLock;
public void createOrder(Long orderId) {
String lockKey = "order_lock_" + orderId;
RLock lock = redissonLock.getLock(lockKey);
// 最多等待1s拿锁,持有最长30s,双重超时防死锁
boolean acquire = lock.tryLock(1000, 30000, TimeUnit.MILLISECONDS);
if (!acquire) {
throw new BusinessException("下单繁忙,请稍后重试");
}
try {
// 所有Redis读写统一遵循200ms基线
} finally {
// 框架封装解锁,捕获Redis超时自动重试释放锁
redissonLock.unlock(lock);
}
}leaseTime,关闭看门狗无限续期;lock(),必须传入 waitTime+leaseTime;故障背景
早期业务未配置idleConnectionTimeout,机房防火墙默认 300s 单向断开闲置 TCP 通道;Redisson 连接池保留大量已被防火墙关闭的失效长连接,后续 GET 请求复用僵死通道,无限阻塞等待响应,业务线程池快速打满,全接口报错。
根因
无闲置连接自动回收机制,TCP 僵死连接无法清理,且命令超时配置过大,线程长期卡死。
修复方案
落地基线idleConnectionTimeout=900000,定时清理超过最小空闲数量的闲置连接,配合 200ms 命令超时快速释放阻塞线程。
故障背景
库存扣减使用 HINCRBY,Redis 瞬时 200ms 超时抛出RedisResponseTimeoutException;开发捕获异常后自行循环重试扣减,而非依赖 Redisson 底层重试,最终库存多扣产生资损。
根因区分
Redisson 底层黑名单不会自动重试 HINCRBY;风险来自业务层手动重试,并非客户端自动重试。
修复规范
库存、计数类操作增加全局唯一流水号幂等校验,捕获 Redis 超时后禁止直接重试。
故障场景
订单服务 Redis 瞬时抖动,finally 内原生unlock()触发 200ms 命令超时,锁始终不释放,30s 内所有下单请求拿锁失败。
修复规范
统一使用框架封装@RedisLock工具类,解锁逻辑增加超时重试兜底。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。