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

JPA PESSIMISTIC_WRITE悲观写锁未按预期生效问题求助

问题排查与解决方案

核心问题分析

你的重复取数问题根源在于查询与数据标记(更新status)的操作不是原子性的,且事务锁的生命周期没有覆盖到关键操作阶段:

  1. 悲观锁仅在查询时生效,但查询完成后事务立即提交(service层方法返回后事务结束),锁被释放,此时第二个实例仍能查询到同一条status为false的数据。
  2. 查询后到更新status之间存在空窗期,两个实例可能先后拿到同一条未标记的数据。
  3. 若MySQL表使用MyISAM存储引擎,会直接忽略行级锁和事务,这也是锁失效的常见原因。

具体解决方案

1. 先确认数据库存储引擎

确保Student表使用InnoDB引擎(MyISAM不支持事务和行级锁),执行SQL检查:

SHOW TABLE STATUS WHERE Name = 'student';

若引擎为MyISAM,执行修改:

ALTER TABLE student ENGINE=InnoDB;

2. 原子化查询+标记操作(推荐方案)

将查询未处理数据和标记为处理中的操作放在同一个事务内,确保锁的生命周期覆盖到标记完成:

步骤1:调整仓库接口

移除仓库方法上的@Transactional,由service层统一管理事务:

@Repository
public interface StudentRepository extends CrudRepository<Student,Integer> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query(value = "SELECT s FROM Student s WHERE s.percentage is null AND s.status = false ORDER BY s.rollid ASC LIMIT 1")
    Student findFirstStudentWithFalseStatusAndNullPercentage();
}
步骤2:修改service层逻辑

在同一个事务内完成查询加锁、标记status的操作:

@Transactional(isolation = Isolation.SERIALIZABLE)
public Student getStudent(){
    Student student = studentRepository.findFirstStudentWithFalseStatusAndNullPercentage();
    if(student != null){
        // 立即标记为处理中,锁将保持到事务提交
        student.setStatus(true);
        studentRepository.save(student);
    }
    return student;
}

3. 更高效的原子更新方案(替代方案)

直接使用UPDATE语句原子性标记数据,避免依赖悲观锁的时机问题:

步骤1:仓库添加更新方法
@Repository
public interface StudentRepository extends CrudRepository<Student,Integer> {
    @Modifying
    @Transactional
    @Query(value = "UPDATE Student s SET s.status = true WHERE s.percentage IS NULL AND s.status = false ORDER BY s.rollid ASC LIMIT 1")
    int markStudentAsProcessing();

    @Query(value = "SELECT s FROM Student s WHERE s.percentage IS NULL AND s.status = true ORDER BY s.rollid ASC LIMIT 1")
    Student findMarkedStudent();
}
步骤2:service层调用逻辑
@Transactional
public Student getStudent(){
    int updatedCount = studentRepository.markStudentAsProcessing();
    if(updatedCount > 0){
        return studentRepository.findMarkedStudent();
    }
    return null;
}

额外注意事项

  • 确保@Scheduled定时任务的执行间隔合理,避免短时间内大量请求导致的锁竞争加剧。
  • Kafka处理完成后,将status改回false的操作也需保证事务性,避免数据状态异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:30:25