You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ShedLock调度任务报错:主键重复,未更新现有行却插入新行

问题原因分析

这个异常的核心原因是**@Transactional注解与ShedLock的锁操作事务冲突**:

当你的定时方法同时标注了@Transactional和@SchedulerLock时,ShedLock的锁获取逻辑(插入/更新shedlock表)会被包裹在方法的事务中。当多个实例同时触发任务时:

  1. 第一个实例执行锁插入操作,但因为事务未提交,这条锁记录还没被其他实例可见;
  2. 其他实例此时尝试插入同主键(myJob)的锁记录,直接触发主键重复的SQL异常;
  3. 第一个实例的事务提交后,锁记录才会持久化,但其他实例已经因为异常退出,不会再执行任务逻辑——这就是为什么你看到任务实际正常(仅一个实例执行),但其他实例抛错的原因。
解决方案

根据你的业务需求,选择以下任意一种方式解决:

1. 移除方法上的@Transactional(优先推荐,如果业务不需要事务)

如果你的任务逻辑不需要事务支持,直接去掉@Transactional注解,让ShedLock的锁操作在独立的事务中执行,这样多个实例会先竞争锁,只有拿到锁的实例才会执行任务逻辑,不会出现插入冲突。

修改后的代码:

@Scheduled(cron = "0 0 1 * * MON")
@SchedulerLock(name = "myJob", lockAtLeastFor = "PT5M")
public void myJob() {
    // 业务代码
}

2. 拆分方法,将锁操作与事务逻辑分离

如果任务逻辑必须使用事务,把带@SchedulerLock的方法和带@Transactional的业务方法拆分:

@Scheduled(cron = "0 0 1 * * MON")
@SchedulerLock(name = "myJob", lockAtLeastFor = "PT5M")
public void myJobWrapper() {
    // 锁操作在独立事务中执行,获取锁后再调用带事务的业务方法
    myJobTransactional();
}

@Transactional
public void myJobTransactional() {
    // 实际业务代码
}

3. 配置ShedLock使用独立事务

如果不想拆分方法,可以给JdbcTemplateLockProvider指定事务管理器,让锁操作在独立事务中执行,不受方法本身的@Transactional影响:

@Bean
public LockProvider lockProvider(DataSource dataSource, PlatformTransactionManager transactionManager) {
    return new JdbcTemplateLockProvider(
            JdbcTemplateLockProvider.Configuration.builder()
                    .withJdbcTemplate(new JdbcTemplate(dataSource))
                    .withTableName("shedlock")
                    .usingDbTime()
                    .withTransactionManager(transactionManager) // 指定独立事务管理器
                    .build()
    );
}

内容的提问来源于stack exchange,提问作者Tudor Cheregi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:47:22