3

エンティティの量が劇的に増加した数日前まで、かなりうまく機能していたインポート ジョブを実行しています。

何が起こるかというと、Lock wait timout を超えたということです。その後、アプリケーションは再試行し、em.getTransaction().begin();を呼び出すため、例外がスローされます。もう1回。

この問題を解決するために、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() + ")" +
                "]";
         }
    }
}
4

2 に答える 2

1

解決策 1. JPA を使用しないでください。大規模なデータベース操作で動作するように設計されていません。DataSource にアクセスでき、トランザクションを手動で管理しているため、単純な古い SQL の使用を妨げるものは何もありません。

解決策 2. 永続コンテキストの第 1 レベル キャッシュに関連するパフォーマンスの問題がある可能性があります。永続化されたすべてのエンティティはそのキャッシュに保持されます。このキャッシュが大きくなると、パフォーマンス (主にメモリ) が低下する可能性があります。

状況を改善するには、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]。テーブルが頻繁にインデックス化されている場合、挿入のパフォーマンスが低下する可能性があります。

それは私の推測です、何かがあなたを助けることを願っています.

于 2012-05-21T19:10:46.580 に答える
0

ピトールはすでにいくつかのオプションを挙げています。彼の「解決策 2」のバリエーションは、セッションとクリアを使用する代わりに、Hibernate StatelessSession API を活用することです。

ただし、他に考慮する必要があるのは、トランザクションとは、合計で失敗または成功すると予想されるステートメントのグループであるということです。多数のステートメントがあり、途中で 1 つのステートメントが失敗し、先行するすべてのステートメントを永続化したい場合は、それらを 1 つのトランザクションにグループ化しないでください。トランザクション内でステートメントを適切にグループ化します。一般に、とにかく Hibernate で jdbc バッチ処理を有効にすることをお勧めします。一般に、データベース通信がより効率的になります。

于 2012-05-21T22:00:34.213 に答える