gpt4 book ai didi

java - Spring :PESSIMISTIC_READ/WRITE 不起作用

转载 作者:行者123 更新时间:2023-12-02 11:06:45 25 4
gpt4 key购买 nike

我有两台服务器连接到同一个数据库。两者都有预定的作业,我并不关心哪一个运行预定的作业,只要只有一个运行即可。因此,我们的想法是在数据库中保留一个键值对,并且无论哪个读取值为 0,第一个都可以运行计划的作业。

理想情况下,它会像这样工作:

  1. 应用 A 和应用 B 同时运行计划作业。
  2. 应用A首先访问数据库,锁定表的读写。
  3. 应用 A 将值设置为 1 并释放锁定。
  4. 应用 A 开始执行预定作业。
  5. 应用 B 从其数据库请求中读取值 1,并且不运行计划作业。

我有一个配置表,用于保存锁的状态。

config:  
name: VARCHAR(55)
value: VARCHAR(55)

存储库:

@Repository
public interface ConfigRepository extends CrudRepository<Config, Long> {
@Lock(LockModeType.PESSIMISTIC_READ)
Config findOneByName(String name);

@Lock(LockModeType.PESSIMISTIC_WRITE)
<S extends Config> S save(S entity);
}

服务:

@Service
public class ConfigService {
@Transactional
public void unlock(ConfigEnum lockable) {
Config lock = configRepository.findOneByName(lockable.getSetting());
lock.setValue("0");
configRepository.save(lock);
}

@Transactional
public void lock(ConfigEnum lockable) {
Config lock = configRepository.findOneByName(lockable.getSetting());
lock.setValue("1");
configRepository.save(lock);
}

@Transactional
public boolean isLocked(ConfigEnum lockable) {
Config lock = configRepository.findOneByName(lockable.getSetting());
return lock.getValue().equals("1");
}
}

调度程序:

@Component
public class JobScheduler {
@Async
@Scheduled("0 0 1 * * *")
@Transactional
public void run() {
if (!configService.isLocked(ConfigEnum.CNF_JOB.getJobName())) {
configService.lock(ConfigEnum.CNF_JOB.getJobName());
jobService.run();
configService.unlock(ConfigEnum.CNF_JOB.getJobName());
}
}
}

但是我注意到计划的作业仍然在两个应用程序上同时运行。有时会引发死锁,但如果遇到死锁,Spring 似乎会重试事务。此时,一个应用程序似乎已完成,因此该应用程序再次开始相同的工作(不确定)。

任务并不短,可以建立锁、更新表、运行任务和释放锁。我希望保持这个非常简单,而不涉及 Quartz 或 ShedLock 等其他库。

最佳答案

我认为您的交易时间太短。您不会在 run 方法中启动事务,但每个 ConfigService 方法都是事务性的。最有可能的是,每个方法都会获得一个新事务并在完成后提交。提交会释放锁,因此 isLocked 和锁之间存在竞争条件。

结合isLocked和lock:

@Transactional
public boolean tryLock(ConfigEnum lockable) {
Config lock = configRepository.findOneByName(lockable.getSetting());
if("1".equals(lock.getValue()) {
return false;
}
lock.setValue("1");
configRepository.save(lock);
return true;
}

这会在同一事务中进行检查和写入,并且应该可以工作。

顺便说一句,这是一种危险的方法。如果拥有锁的节点死亡会发生什么?有很多可能的解决方案。一种是锁定特定记录并在整个作业过程中保持该锁定。另一个节点无法继续,如果第一个节点死亡,锁将被释放。另一种是使用时间戳而不是 1,并要求所有者定期更新时间戳。或者你可以引入像 Zookeeper 这样的东西。

关于java - Spring :PESSIMISTIC_READ/WRITE 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50899370/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com