shedlock 通过 redis 的 set key value ex seconds nx 原子命令实现加锁,键为 shedlock:{任务名},值为节点标识,ttl 由 lockatmostfor 控制;重复执行主因是 redis 配置不一致、集群未适配、绕过 aop 或 ttl 过短。

ShedLock 本身不解决分布式锁的底层问题,它只是把 Redis(或其他存储)封装成任务锁的抽象层;真正防重的关键是 Redis 的原子操作和锁过期机制,不是 ShedLock 自己“保证不重复”。
ShedLock 怎么用 Redis 实现加锁
ShedLock 启动后,每个定时任务执行前会尝试在 Redis 写入一条带 TTL 的记录,键名格式为 shedlock:{任务名},值为当前节点标识(如主机名+进程ID)。加锁成功 = Redis 返回 OK 且该 key 原本不存在(靠 SET key value EX seconds NX 原子命令保证)。
- 必须用 Redis 的
SET ... NX EX,不能用SETNX + EXPIRE组合,否则有竞态漏洞 - TTL 值由
@SchedulerLock(annotation)的lockAtMostFor参数控制,建议设为任务最长可能耗时的 2–3 倍,避免任务未完成锁就过期 - 如果任务卡死、JVM 崩溃,锁会在 TTL 到期后自动释放,这是防死锁的基础保障
为什么加了 @SchedulerLock 还会重复执行
常见原因不是 ShedLock 失效,而是配置或使用方式破坏了它的前提条件:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 多个应用实例用了不同的 Redis 数据库(
database配置不一致),导致锁不在同一个命名空间里 - Redis 是集群模式但没启用
redisson或lettuce的集群感知能力,ShedLock 默认只连一个节点,跨节点锁失效 - 任务方法被其他非
@Scheduled方式调用(比如单元测试、HTTP 接口直接触发),绕过了 ShedLock 的 AOP 拦截 -
lockAtMostFor设得太小,任务偶尔超时,锁提前释放,下一个周期立刻抢到锁并并发执行
ShedLock + Redis 的性能与兼容性注意点
每次任务触发都要走一次 Redis 写操作,高频率任务(如每秒执行)容易打满 Redis 连接或产生延迟。这不是 ShedLock 的锅,但得你来权衡:
- ShedLock 4.x 起默认用
Lettuce客户端,支持连接池和异步命令;别用老版本配Jedis,连接复用差 - Redis 版本低于 2.6.12 不支持
SET ... NX EX,会降级为SETNX + EXPIRE,此时必须确保这两条命令在同一个连接上顺序执行(Lettuce 可配syncCommands,Jedis 则需用Pipeline) - 如果 Redis 出现网络分区或响应超时,ShedLock 默认抛
LockingException并跳过本次执行——这点很关键,意味着你要确认日志里有没有大量Could not acquire lock报错
最常被忽略的是:ShedLock 的锁只对加了 @SchedulerLock 注解的方法生效,它不会管你的线程池、CompletableFuture 或手动 new Thread();防重边界只在 Spring 定时调度这一层,别指望它保护整个业务逻辑块。










