2017-08-08 81 views
1

我有一個調度程序與多個線程更新我的表中的行。我試圖使用「更新跳過鎖定」,但由於某種原因,而不是找到下一個無人認領的行,每個線程選擇並更新相同的一個。 我有一個表「計數器」與「ID」和「計數器」列。Postgres「跳過鎖定」與Spring JDBC

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

您使用的是什麼交易級別?您需要在行上讀取鎖以使邏輯工作。 – Voo

+0

@Voo它的默認閱讀承諾 –

+0

那麼你知道爲什麼這是行不通的。 – Voo

回答

0

你寫的應該可以正常使用默認的隔離級別的SQL。 該問題可能與初始數據或用於更新「計數器」的邏輯有關。

查看「計數器」更新的邏輯,您將每次都將counter設置爲5 - counter。這意味着該值將在一組固定的兩個值之間持續切換。例如。如果對於給定的ID,最初的counter爲0,程序將保持在5和0之間的值。我希望這是按預期的。如果是這樣的話,一種可能性(給出這個例子中的數據)是所有其他行都有一個counter值> 5 AND JVM一次只運行一個線程。通過這種方式,被更新的行將總是具有最低的計數器,並且在該行被鎖定時,沒有其他線程會嘗試選擇另一行。這似乎很少見,但在理論上是可能的。

我建議 -

  1. 每一個線程進入一次登錄的消息,離開update()的方法來檢查,如果多個線程確實在同一時間
  2. 進入方法檢查在初始數據該表格並確保計數器更新邏輯在給定此數據的情況下可以正常工作(隨時也可以在此發佈數據)