web-dev-qa-db-ja.com

JPA + Hibernateを使用した大規模なインサート

EJB 3、Hibernate、Spring Data、およびOracleを使用して大規模な挿入を行う必要があります。もともと、私はSpring Dataを使用しており、コードは以下のとおりです。

_talaoAITDAO.save(taloes);
_

TalaoAITDAOはSpringData JpaRepository サブクラスであり、taloesはTalaoAITエンティティのコレクションです。このエンティティでは、それぞれのIDは次の形式になります。

_@Id
@Column(name = "ID_TALAO_AIT")
@SequenceGenerator(name = "SQ_TALAO_AIT", sequenceName = "SQ_TALAO_AIT", allocationSize = 1000)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_TALAO_AIT")
private Long id;
_

また、このエンティティには、カスケード挿入を行うための関連エンティティがありません。

ここでの私の問題は、すべてのエンティティが個別に挿入されることです(INSERT INTO TABLE(col1, col2) VALUES (val1, val2)など)。場合によっては、タイムアウトが発生し、すべての挿入がロールバックされることがあります。これらの個々の挿入をバッチ挿入(INSERT INTO TABLE(col1, col2) VALUES (val11, val12), (val21, val22), (val31, val32), ...など)に変換したいと思います。

パフォーマンスを改善するための代替案を検討していると、 このページ が休止状態のドキュメントにあり、 休止状態のバッチサイズの混乱 および この他のページ 。それらに基づいて、私はこのコードを書きました:

_Session session = super.getEntityManager().unwrap(Session.class);
int batchSize = 1000;
for (int i = 0; i < taloes.size(); i++) {
    TalaoAIT talaoAIT = taloes.get(i);
    session.save(talaoAIT);
    if(i % batchSize == 0) {
        session.flush();
        session.clear();
    }
    taloes.add(talaoAIT);
}
session.flush();
session.clear();
_

また、peristence.xmlで、次のプロパティを追加しました。

_<property name="hibernate.jdbc.batch_size" value="1000" />
<property name="order_inserts" value="true" />
_

しかし、私のテストでは、微妙な違い(主に大きなコレクションと大きなバッチサイズ)を認識していましたが、それは望ましいほど大きくはありませんでした。ロギングコンソールで、Hibernateが引き続き個別の挿入を実行し、大量の挿入に置き換えるのではないことを確認しました。私のエンティティと同様に、問題ではないと思われるシーケンスジェネレーターを使用しています(Hibernateのドキュメントによると、IDジェネレーターを使用していると問題が発生します)。

だから、私の質問はここで何が欠けている可能性があるかです。いくつかの構成?使用されていない方法はありますか?

ありがとう、

ラファエル・アフォンソ。

11
Rafael Afonso

いくつかのこと。

まず、構成プロパティが間違っていますorder_inserts でなければなりません hibernate.order_inserts。現在、設定は無視され、何も変更されていません。

次に、厄介な休止状態の作業をすべて行う代わりに、EntityManagerを使用します。 EntityManagerにはflushメソッドとclearメソッドもあります。これにより、少なくともメソッドがクリーンアップされます。順序がないと、これはセッションをクリーンアップし、そこにあるすべてのオブジェクトのダーティチェックを防ぐのに少し役立ちます。

EntityManager em = getEntityManager();
int batchSize = 1000;
for (int i = 0; i < taloes.size(); i++) {
    TalaoAIT talaoAIT = taloes.get(i);
    em.persist(talaoAIT);
    if(i % batchSize == 0) {
        em.flush();
        em.clear();
    }
    taloes.add(talaoAIT);
}
em.flush();
em.clear();

次に、メモリの問題を引き起こす可能性があるため、バッチを大きくしないでください。50などから始めて、どれが最もパフォーマンスが高いかをテストします。ダーティチェックは、データベースへのフラッシングとクリアよりも時間がかかるポイントがあります。このスイートスポットを見つけたいと思います。

15
M. Deinum

M. Deinumによって投稿されたソリューションは、JPAで次のHibernateプロパティを設定した場合に、非常にうまく機能しましたpersistence.xmlファイル:

<property name="hibernate.jdbc.batch_size" value="50" />
<property name="hibernate.jdbc.batch_versioned_data" value="true" />
<property name="hibernate.order_inserts" value="true" />
<property name="hibernate.order_updates" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
<property name="hibernate.connection.autocommit" value="false" />

私はOracleデータベースを使用しているので、これも定義しています。

<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
1
Jim Tough

最近、HibernateとPostgresqlを使用して挿入をバッチ処理するための有望な小さなライブラリを見つけました。これは pedal-dialect と呼ばれ、Postgresql--コマンドCOPYを使用します。これは、バッチ挿入よりもはるかに高速であると多くの人が主張しています(参照: PostgresqlマニュアルPostgresql Insert Strategies-Performance Testコピーはどのように機能し、なぜ挿入よりもはるかに高速なのですか? )。ペダル方言を使用すると、Hibernateの使いやすさを完全に失うことなくCOPYを使用できます。エンティティと行の自動マッピングは引き続き取得でき、独自に実装する必要はありません。

0
mm759