7

データベース内の20K製品を更新するためにHibernateを使用しています。

今のところ、私は20K製品を取り込んで、それらをループし、いくつかのプロパティを変更してから、データベースを更新しています。

それで:

load products

foreach products
   session begintransaction
   productDao.MakePersistant(p);
   session commit();

今のところ、標準のjdbcに比べて物事はかなり遅いですが、物事をスピードアップするために何ができますか?

私はここで何か間違ったことをしていると確信しています。

4

4 に答える 4

10

この種の処理のドキュメントを見るのに適切な場所は、第13章全体です。バッチ処理

ここで、現在のアプローチにはいくつかの明らかな間違いがあります。

  • 更新ごとにトランザクションを開始/コミットしないでください。
  • JDBCバッチ処理を有効にして、適切な数(10〜50)に設定する必要があります。

    hibernate.jdbc.batch_size 20
    
  • flush()その後、定期的にセッションを実行する必要がありclear()ます(nがパラメータに等しいnレコードごと) 。そうしないと、セッションは成長し続け、ある時点でhibernate.jdbc.batch_size爆発する可能性があります( )。OutOfMemoryException

以下、セクション13.2に示されている例。これを示すバッチ更新

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")
    .setCacheMode(CacheMode.IGNORE)
    .scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while ( customers.next() ) {
    Customer customer = (Customer) customers.get(0);
    customer.updateStuff(...);
    if ( ++count % 20 == 0 ) {
        //flush a batch of updates and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

StatelessSessionの使用を検討することもできます。

もう1つのオプションは、DMLスタイルの操作(HQLで!)を使用することですUPDATE FROM? EntityName (WHERE where_conditions)?。これはHQLUPDATEの例です。

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
tx.commit();
session.close();

繰り返しになりますが、詳細(特にキーワードを使用してversionまたはtimestampプロパティ値を処理する方法)については、ドキュメントを参照してください。VERSIONED

于 2010-03-07T22:53:08.097 に答える
5

これが擬似コードである場合は、トランザクションをループの外に移動することをお勧めします。または、1つのトランザクションにすべての20K製品を含めることが多すぎる場合は、少なくとも二重ループを使用することをお勧めします。

load products
foreach (batch)
{
   try
   {
      session beginTransaction()
      foreach (product in batch)
      {
          product.saveOrUpdate()
      }
      session commit()
   }
   catch (Exception e)
   {
       e.printStackTrace()
       session.rollback()
   }
}

また、UPDATEを個別にデータベースに送信するのではなく、バッチ処理することをお勧めします。その方法ではネットワークトラフィックが多すぎます。各チャンクを1つのバッチにまとめて、一度に送信します。

于 2010-03-07T22:44:19.107 に答える
1

バッチ処理の章を見ることについての上記の答えに同意します。

また、製品に必要な変更に必要なものだけをロードするようにする必要があることも付け加えておきたいと思います。

つまり、製品がこのトランザクションにとって重要ではない他の多数のオブジェクトを熱心にロードする場合は、結合されたオブジェクトをロードしないことを検討する必要があります。これにより、製品のロードが高速化され、永続化戦略によっては、製品を再び永続化するときに時間を節約できます。

于 2010-03-08T13:49:47.673 に答える
0

バッチ更新を実行するための最速の方法は、それを単一のSQLステートメントに変換し、セッションで生のSQLとして実行することです。何かのようなもの

update TABLE set (x=y) where w=z;

トランザクションを減らして更新をバッチで実行できることに失敗した場合:

start session
start transaction

products = session.getNamedQuery("GetProducs")
    .setCacheMode(CacheMode.IGNORE)
    .scroll(ScrollMode.FORWARD_ONLY);
count=0;
foreach product
    update product
    if ( ++count % 20 == 0 ) {
        session.flush();
        session.clear();
    }
}

commit transaction
close session

詳細については、HibernateCommunityDocsを参照してください。

于 2010-03-07T22:49:58.650 に答える