2017-05-20 58 views
0

我有兩個步驟Spring Batch的:替代JpaPagingItemReader導致ORA-01555

  1. 步驟1批作業:轉到外部數據庫,調用存儲過程,編寫JPA實體並持續到內部數據庫的標誌NOT_PROCESSED。
  2. 第2步:遍歷剛纔保存的實體標誌NOT_PROCESSED,對它們進行處理,並寫更新實體回(不更新標誌)

一旦所有的人都被處理的標誌個個已更新爲已處理。即全部或全部更新。

第1步是好的,工作很順利。

第2步基本上是JpaPagingItemReader,其中pageSize = 4,處理器集合(主要是http調用)和JpaItemWriter,其中commit-interval = 1。 (我知道建議使pageSize等於commit-interval,這正是我所擁有的)。它也是一個多線程的步驟,包含10個線程完成這項工作。

這在第2步說,我有兩個類型的查詢:

  1. 閱讀:select * from ENTITY where processed=false order by id嵌套到兩個查詢分頁select ... from (select .. where rownum < M) where rownum >= N

  2. 寫:update ENTITY set .. where id = ID

對於某些原因,當我有足夠的實體我臭名昭着:

ORA-01555,快照太舊:回滾段名稱爲「」 太小

我不知道確切的錯誤的(撤消統計並沒有顯示什麼不好的原因,所以希望數據庫管理員將很快找到罪魁禍首),但同時我認爲讀取查詢的功能非常糟糕。這樣的分頁查詢對於數據庫來說很難,但是我猜你讀的時候,同時更新你讀的條目可能會導致這種錯誤。

我想改變步驟2中採取的方法,而不是閱讀頁面。我想將所有ID都讀入內存只有一次(即給我所有我需要處理的實體的ID),然後給每個線程從該列表中的ID。鏈中的第一個處理器將通過JPA通過id獲取實體。這樣我就可以逐一更新和寫入實體,同時我只需要閱讀一次我只需要的ID。

我的問題是我找不到這種閱讀器的開箱即用解決方案。有什麼我可以使用的嗎?

+0

爲什麼不執行select + update作爲單個SQL語句?另外什麼是你的隔離級別? – ibre5041

+0

由於要獲取更新值,如果我簡化了一個非常複雜的邏輯,則更新取決於外部服務調用。 oracle默認,afair讀取提交。 –

+0

你可以得到這個錯誤的原因主要有兩個,第一是修改lob時,第二是你的查詢執行花費太多時間。 「太多」通常定義爲視圖v $ undostat中的列TUNED_UNDORETENTION。這將顯示什麼是合理的撤銷數據庫保留,特別是UNDO tbs大小,以及特定的事務活動。 – ibre5041

回答

0

那麼,我自己實施瞭解決方案,它基於thisthis。實際上我並沒有直接使用這些,但是我的實現非常接近。

基本上,這是它的外觀(我沒有代碼,所以用我的記憶)

public class MyUnprocessedIdReader extends AbstractItemCountingItemStreamItemReader<Long> { 

    private final Object lock = new Object(); 

    private initialized = false; 

    private final MyObjectsRepository repo; 

    private List<Long> ids; 

    private int current = -1; 

    public MyUnprocessedIdReader(MyObjectsRepository repo) { 
     this.repo = repo; 
    } 

    public void doOpen() { 
     synchronized(lock) { 
      Assert.state(!initialized, "Cannot open an already opened ItemReader, call close first"); 

      this.initialized = true; 
      this.ids = ImmutableList.copyOf(repo.findAllUnprocessed()); 
     } 
    } 

    public Long doRead() { 
     synchronized(lock) { 
      if (ids == null || !initialized) { 
      throw new IllegalStateException("Have you opened the reader?"); 
      } 

      ++current; 
      if (current < ids.size()) { 
       return ids.get(current); 
      } else { 
       return null; 
      } 
     } 
    } 

    public void doClose() { 
     synchronized(lock) { 
      this.initialized = false; 
      this.current = -1; 
      this.ids = null; 
     }  
    } 
} 

我的倉庫使用JPA這樣引擎蓋下它使用類似於entityManager.createQuery("select obj.id from Objects where obj.processed = false order by obj.id asc", Long.class).executeSelect()

此外,我還增加了一個處理器鏈:

public class LoadProcessor implements ItemProcessor<Long, MyObject> { 
    private final MyObjectsRepository repo; 

    public LoadProcessor(MyObjectsRepository repo) { 
     this.repo = repo; 
    } 

    public MyObject process(Long id) { 
     return repo.findById(id); 
    } 
} 

有人可能會說,這是比使用光標少可擴展的,也有在讀一個方法具爭但是,這是非常簡單的解決方案,它能夠很好地工作,直到未處理的ID數量過大。處理線程也花費大量時間調用外部REST服務,因此讀取爭用不會成爲有史以來的瓶頸。

P.s.稍後我會提供一個關於ORA-01555是否解決問題的更新。