2012-05-21 6 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, он не предназначен для работы с массивными операций с базами данных. Поскольку у вас есть доступ к вашему DataSource, и вы управляете транзакциями вручную, ничто не мешает вам использовать простой старый SQL.

Решение 2. Там может быть проблема производительности связана с сохранением контекста кэша первого уровня - каждый сохранялось объект хранится в этом кэше, когда кэш становится большим, что может повредить производительности (в основном памяти)

Чтобы улучшить ситуацию, установите свойство hibernate.jdbc.batch_size (или эквивалент, если вы не используете Hibernate-реализацию JPA) более или менее 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. Tune MySQL InnoDB двигатель [http://dev.mysql.com/doc/refman/5.1/en/insert-speed.html, HTTP: //dev.mysql. ком/DOC/RefMan/5.0/ен/InnoDB-tuning.html]. Если таблица сильно индексирована, это может повредить производительность вставки.

Это мои предположения, надеюсь, что-то вам поможет.

+0

Благодарим за советы – jakob

0

Pitor уже назвал пару вариантов. Я хотел бы указать, что вариация его «Решения 2» будет заключаться в том, чтобы использовать Hibernate StatelessSession api вместо использования сеанса и очистки.

Однако, что-то еще, что вы должны учитывать, это то, что транзакция представляет собой группировку операторов, которые, как ожидается, потерпят неудачу или достигнут успеха. Если у вас есть куча операторов, а одна в середине терпит неудачу, и вы хотите, чтобы все предыдущие утверждения были постоянными, вы не должны группировать их в одну транзакцию. Группируйте свои заявления правильно в транзакциях.Как правило, рекомендуется включить jdbc-пакет в Hibernate; он обычно ведет к более эффективному обмену данными с базой данных.

Смежные вопросы