我有一个包含多个线程的调度程序,用于更新表中的行。我尝试使用"更新跳过锁定",但由于某种原因,而不是找到下一个无人认领的行,每个线程选择并更新相同的行。 我有一张桌子" counter"用" id"和"计数器"列。
CounterRepository.java
@Repository
public class CounterRepository {
private final JdbcTemplate jdbc;
public CounterRepository(JdbcTemplate jdbc) {this.jdbc = jdbc;}
@Transactional
void update() {
Integer id = jdbc.queryForObject("SELECT id FROM counter ORDER BY counter LIMIT 1 FOR UPDATE SKIP LOCKED;", Integer.class);
Integer counter = jdbc.queryForObject("SELECT counter FROM counter WHERE id = ?", Integer.class, id);
jdbc.update("UPDATE counter set counter = ? WHERE id = ?", 5 - counter, id);
}
}
DbAsyncUpdateApplication.java
@EnableAsync
@EnableScheduling
@SpringBootApplication
public class DbAsyncUpdateApplication {
@Bean(name = "threadPoolTaskExecutor")
public ScheduledExecutorService taskScheduler() throws InterruptedException {
return Executors.newScheduledThreadPool(5);
}
public static void main(String[] args) {
SpringApplication.run(DbAsyncUpdateApplication.class, args);
}
}
Scheduler.java
@Service
public class Scheduler {
private final CounterRepository repository;
public Scheduler(CounterRepository repository) {this.repository = repository;}
@Scheduled(fixedRate = 1000)
public void start() {
repository.update();
}
}
答案 0 :(得分:0)
您编写的SQL应该可以正常使用默认的隔离级别。 问题可能是初始数据或用于更新“计数器”的逻辑。
查看“计数器”更新的逻辑,您每次都会将counter
设置为5 - counter
。这意味着该值将在一组固定的两个值之间保持切换。例如。如果对于给定的id,初始counter
为0,则程序将保持在5和0之间切换值。我希望这是预期的。如果是这样,一种可能性(给定本例中的数据)是所有其他行具有counter
值> 5 AND JVM一次只运行1个线程。这样,正在更新的行将始终具有最低的计数器,并且在此行被锁定时,没有其他线程会尝试选择另一行。这似乎很少见,但理论上是可行的。
我建议 -
update()
方法以检查多个线程是否确实同时进入该方法