2012-05-21 19 views
3

我正在運行一項導入作業,直到幾天前實體數量急劇增加時才運行良好。如何處理以正確方式超出鎖定等待時間?

發生什麼事是我得到一個鎖等待超時。由於我調用了em.getTransaction()。begin();因此應用程序重試並拋出異常。再一次。

爲了擺脫這個問題,我將innodb_lock_wait_timeout更改爲120,並將 降低到50個實體。

我弄不清楚的是如何在代碼中正確處理所有這些。我不希望整個導入因鎖定而失敗。你將如何處理?你有任何代碼 的例子?也許還有其他想法?請堅果!

我BatchPersister:

public class BatchPersister implements Persister { 

    private final static Log log = getLog(BatchPersister.class); 
    private WorkLogger workLog = WorkLogger.instance(); 

    private static final int BATCH_SIZE = 500; 

    private int persistedObjects; 
    private long startTime; 
    private UpdateBatch batch; 
    private String dataSource; 


    public BatchPersister(String dataSource) { 
     this.dataSource = dataSource;   
    } 

    public void persist(Persistable obj) { 

     persistedObjects++; 
     logProgress(100); 

     if (batch == null) 
      batch = new UpdateBatch(BATCH_SIZE, dataSource); 

     batch.add(obj); 

     if (batch.isFull()) { 
      batch.persist(); 
      batch = null; 
     } 
    } 
} 

的UpdateBatch

public class UpdateBatch { 

    private final static Log log = LogFactory.getLog(UpdateBatch.class); 
    private WorkLogger workLogger = WorkLogger.instance(); 

    private final Map<Object, Persistable> batch; 
    private final EntityManager em; 
    private int size; 

    /** 
    * Initializes the batch and specifies its size. 
    */ 
    public UpdateBatch(int size, String dataSource) { 
     this.size = size; 
     batch = new LinkedHashMap<Object, Persistable>(); 
     em = EmFactory.getEm(dataSource); 
    }  

    public void persist() { 
     log.info("Persisting " + this); 
     em.getTransaction().begin();  
     persistAllToDB(); 
     em.getTransaction().commit(); 

     WorkLog batchLog = new WorkLog(IMPORT_PERSIST, IN_PROGRESS); 
     batchLog.setAffectedItems(batch.size()); 
     workLogger.log(batchLog); 
     em.close(); 
    } 

/** 
    * Persists all data in this update batch 
    */ 
    private void persistAllToDB() { 
     for (Persistable persistable : batch.values()) 
      em.persist(persistable); 
     } 

     @Override 
     public String toString() { 
      final ArrayList<Persistable> values = new ArrayList<Persistable>(batch.values()); 
      Persistable first = values.get(0); 
      Persistable last = values.get(values.size() - 1); 
      return "UpdateBatch[" + 
       first.getClass().getSimpleName() + "(" + first.getId() + ")" + 
       " - " + 
       last.getClass().getSimpleName() + "(" + last.getId() + ")" + 
       "]"; 
     } 
    } 
} 

回答

1

解決方法1. 不要使用JPA,它不是設計有大量的數據庫操作工作。既然你可以訪問你的數據源,並且你正在手動管理事務,那麼沒有什麼能夠阻止你使用普通的舊SQL。

解決方案2. 有可能與持久化上下文一級緩存連接的性能問題 - 當這個緩存變大,可能損害性能(主要是內存)

每個實體持續保持在高速緩存中,爲了改善情況,設置hibernate.jdbc.batch_size屬性(或者等價的,如果你沒有使用JPA的Hibernate實現)到20或更多 - 由於這些查詢將發送到20個查詢包中的數據庫。其次,每20次操作清理一次持久化上下文,強制與數據庫同步。

private void persistAllToDB() { 
    int counter = 0; 
    for (Persistable persistable : batch.values()) 
     em.persist(persistable); 
     counter++; 
     if(counter % 20 == 0){ 
      em.flush(); 
      em.clear(); 
     } 
    } 
} 

解決方案3. 調MySQL的InnoDB引擎[http://dev.mysql.com/doc/refman/5.1/en/insert-speed.html,HTTP://dev.mysql。 COM/DOC/refman/5.0/EN/innodb的-tuning.html]。如果您的表格索引嚴重,可能會損害插入性能。

這是我的猜測,希望有些事情能幫助你。

+0

謝謝你的提示 – jakob

0

Pitor已經命名了幾個選項。我會指出他的「解決方案2」的一個變種是利用Hibernate StatelessSession API,而不是使用Session和清除。

但是,您應該考慮的其他事情是事務是一組預期會失敗或成功完成的語句。如果你有一堆陳述,中間有一個失敗,而你希望前面的所有陳述都是持久的,那麼你不應該把它們在一個事務中分組。在交易中正確分組您的報表。通常,在Hibernate中啓用jdbc批處理是一個好主意;它通常會導致更高效的數據庫通信。

相關問題