我正在运行一个导入作业,直到几天前实体数量急剧增加时,它一直运行良好。
发生的事情是我收到了超出锁定等待超时的消息。然后应用程序重试并抛出异常,因为我再次调用了 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;
}
}
}
批量更新
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. 不要使用 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]。如果您的表被大量索引,则可能会影响插入性能。
以上是我的猜测,希望对你有所帮助。
关于java - 如何以正确的方式处理超过锁定等待超时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10686809/