Postgres" SKIP LOCKED"使用Spring JDBC

时间:2017-08-08 13:22:23

标签: java spring multithreading postgresql jdbc

我有一个包含多个线程的调度程序,用于更新表中的行。我尝试使用"更新跳过锁定",但由于某种原因,而不是找到下一个无人认领的行,每个线程选择并更新相同的行。 我有一张桌子" 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();
    }
}

1 个答案:

答案 0 :(得分:0)

您编写的SQL应该可以正常使用默认的隔离级别。 问题可能是初始数据或用于更新“计数器”的逻辑。

查看“计数器”更新的逻辑,您每次都会将counter设置为5 - counter。这意味着该值将在一组固定的两个值之间保持切换。例如。如果对于给定的id,初始counter为0,则程序将保持在5和0之间切换值。我希望这是预期的。如果是这样,一种可能性(给定本例中的数据)是所有其他行具有counter值> 5 AND JVM一次只运行1个线程。这样,正在更新的行将始终具有最低的计数器,并且在此行被锁定时,没有其他线程会尝试选择另一行。这似乎很少见,但理论上是可行的。

我建议 -

  1. 每次线程进入时都记录一条消息并离开update()方法以检查多个线程是否确实同时进入该方法
  2. 检查表格中的初始数据,并确保计数器更新逻辑在此数据下可以正常工作(也可以在此处发布数据)