この記事では、大規模なデータの冬眠バッチ処理の方法について説明します。次のように、参照のために共有してください。
Hibernateバッチ処理大量の量は、実際にはパフォーマンスの観点からは望ましくなく、多くの記憶が無駄になります。そのメカニズムから、Hibernateは最初に条件を満たし、それをメモリに置き、操作を実行するデータを最初にチェックアウトします。パフォーマンスは、実際に使用するには非常に不十分です。私の実際の使用では、次の3番目の最適化ソリューションのデータは次のとおりです。100,000個のデータがデータベースに挿入され、約30分かかります。ハハ、かすか。 (10分で1000,000個のデータを挿入しました(フィールドは比較的小さい))
パフォーマンスの問題を解決するために対処するには3つの方法があります。
1:Hibernate APIをバイパスし、JDBC APIを直接使用します。この方法はパフォーマンスが向上します。また、最速です。
2:ストアドプロシージャを使用します。
3:Hibernate APIを使用して、通常のバッチ処理を実行します。変更がある可能性があり、変更は変化します。一定の金額が見つかったら、操作を完了した後、時間内にデータを削除できます。Session.Flush(); session.evict(xx object set);これにより、パフォーマンスの損失も節約できます。この「一定の量」は、実際の条件に基づく定量的参照として使用する必要があります。一般的に30〜60程度ですが、その効果はまだ理想的ではありません。
1:Hibernate APIをバイパスし、JDBC APIを介して直接実行します。この方法はパフォーマンスが向上し、最速です。 (例は更新操作です)
トランザクションtx = session.begintransaction(); // Hibernateトランザクション境界接続を使用していることに注意してください。 preatedStatement stmt = conn.preparedStatement( "customerをc set c.sarlary = c.sarlary+1 c.sarlary> 1000"); stmt.excuteUpdate(); tx.commit(); // Hibernateトランザクション境界を使用していることに注意してください
このアプレットでは、JDBCを直接呼び出すAPIを使用してデータベースにアクセスします。これは非常に効率的です。 Hibernateの最初のクエリとメモリへの読み込みによるパフォーマンスの問題を避け、操作を実行します。
2:ストアドプロシージャを使用します。ただし、この方法は、移植性とプログラムの展開の利便性のために使用することをお勧めしません。 (例は更新操作です)
基礎となるデータベース(Oracleなど)がストアドプロシージャをサポートする場合、バッチの更新もストアドプロシージャを通じて実行できます。ストアドプロシージャは、データベースで直接実行されます。 Oracleデータベースでは、batchupdatecustomer()という名前のストアドプロシージャを定義できます。コードは次のとおりです。
コードコピーは次のとおりです。手順を作成または交換しますbatchupdatecustomer(number in number)as begin update顧客を設定します= age+1 where age> p_age; end;
上記のストアドプロシージャには、クライアントの年齢を表すパラメーターP_AGEがあります。アプリケーションは、次の方法でストアドプロシージャを呼び出すことができます。
tx = session.begintransaction(); connection con = session.connection(); string procedure = "{call batchupdatecustomer(?)}"; callablestatement cstmt = con.preparecall(procedure); cstmt.setint(1、0); //年齢パラメーターを0cstmt.executeupdate(); tx.commit()に設定します。上記のプログラムからわかるように、アプリケーションはHibernate APIをバイパスし、JDBC APIを介してストアドプロシージャを直接呼び出す必要があります。
3:Hibernate APIを使用して、通常のバッチ処理を実行します。変更がある可能性があり、変更は変化します。一定の金額が見つかったら、操作を完了した後、時間内にデータを削除できます。Session.Flush(); session.evict(xx object set);これにより、パフォーマンスの損失も節約できます。この「一定の量」は、実際の条件に基づいて定量的な参照である必要があります...
(例は保存操作です)
ビジネスロジックは次のとおりです。データベースに10 0000個のデータを挿入したい
tx = session.begintransaction(); for(int i = 0; i <100000; i ++){customer custom = new customer(); custom.setname( "user"+i); sessive.save(custom); if(i%50 == 0); if(i%50データを処理ユニットとして使用します。これにより、システムは安定した範囲になります...
プロジェクト開発プロセス中、プロジェクトの要件により、多くの場合、大量のデータをデータベースに挿入する必要があります。数万、数千万、数千万、さらには数千万もあります。このレベルのマグニチュードのデータを挿入するためにHibernateを使用すると、例外が発生する可能性があります。一般的な例外は、OutOfMemoryError(メモリオーバーフロー例外)です。
まず、冬眠挿入操作のメカニズムを簡単に確認しましょう。 Hibernateは内部キャッシュを維持する必要があります。挿入操作を実行すると、すべてのオブジェクトを内部キャッシュで動作させるために動作させます。
Hibernateのキャッシュに関しては、Hibernateには内部キャッシュとセカンダリキャッシュの理論があります。 Hibernateにはこれら2つのキャッシュの管理メカニズムが異なるため、セカンダリキャッシュに関連してサイズを構成できますが、内部キャッシュの場合、Hibernateは「リーシュストリーミング」態度を採用し、その能力に制限はありません。これで、問題の核心が見つかりました。大規模なデータを挿入すると、多くのオブジェクトが内部キャッシュに含まれます(内部キャッシュはメモリにキャッシュされます)。そのため、システムメモリは少しずつ食べられます。システムが最終的に「揚げ」になった場合、それは合理的です。
この問題にもっと対処する方法について考えてみましょうか?いくつかの開発条件は、Hibernateを使用して処理する必要があります。もちろん、一部のプロジェクトはより柔軟であり、他の方法を見つけることができます。
ここでは、2つの方法をお勧めします。
(1):冬眠を最適化し、セグメント化された挿入の方法を使用して、プログラムの時間内にキャッシュをクリアします。
(2):Hibernate APIをバイパスし、JDBC APIを介して直接バッチ挿入を行います。この方法は最高のパフォーマンスと最速です。
上記の方法1の場合、基本的なアイデアは次のとおりです。冬眠を最適化し、構成ファイルにhibernate.jdbc.batch_sizeパラメーターを設定して、毎回送信されるSQLの数を指定します。このプログラムは、セグメント化された挿入で時間内にキャッシュをクリアする方法を使用します(セッションは非同期書き込み式を実装します。これにより、冬眠は操作を明示的に書き込むことができます)。つまり、一定量のデータを挿入した後の時間内に内部キャッシュからクリアし、占有されたメモリを解放します。
hibernate.jdbc.batch_sizeパラメーターを設定するには、次の構成を参照できます。
<hibernate-configuration> <session-factory>…<プロパティ名= "hibernate.jdbc.batch_size"> 50 </property>…<session-factory> <hibernate-configuration>
hibernate.jdbc.batch_sizeパラメーターを構成する理由は、データベースをできるだけ読み取ることです。 hibernate.jdbc.batch_sizeパラメーターの値が大きいほど、データベースを読む時間が少なくなり、速度が速くなります。上記の構成から、プログラムが50 SQLを蓄積するまで、バッチで送信するまで、Hibernateが待機することがわかります。
著者はまた、hibernate.jdbc.batch_sizeパラメーターの値はできるだけ大きく設定されていない可能性があり、パフォーマンスの観点からはまだ議論されていないと考えています。これには、実際の状況を考慮し、適切に設定する必要があります。一般的に、30または50の設定はニーズを満たすことができます。
プログラムの実装に関しては、著者は10,000個のデータの挿入を例として取ります。
セッションセッション= hibernateutil.currentsession(); transatcion tx = session.begintransaction(); for(int i = 0; i <10000; i ++){desute st = new Student( "feifei"); session.save(st); if(i%50 == 0) //データベースデータsession.clear()と同期してください。 //すべてのデータを内部的にキャッシュし、時間内に占有されたメモリをリリースします}} tx.commit(); ...特定のデータスケールでは、このアプローチはシステムメモリリソースを比較的安定した範囲で維持できます。
注:前述の第2レベルのキャッシュは、ここで言及するために必要です。セカンダリキャッシュが有効になっている場合、セカンダリキャッシュを維持するために、Hibernateは、操作を挿入、更新、削除すると、対応するデータをセカンダリキャッシュに充電します。パフォーマンスには大きな損失が発生するため、著者はバッチ処理でレベル2キャッシュを無効にすることを推奨します。
方法2では、従来のJDBCバッチ処理が使用され、JDBC APIが使用されてそれを処理します。
Java Batch ProcessingおよびSelfExecution SQLを参照してください。
上記のコードを見ると、何かが不適切だといつも感じていますか?はい、気づきませんでした!これは依然としてJDBCの伝統的なプログラミングであり、冬眠の風味はありません。
上記のコードは、次のように変更できます。
トランザクションtx = session.begintransaction(); // Hibernate Transaction Processing Connection conn = session.connection()を使用します。 prepareStatement stmt = conn.preparestatement( "t_student(name)values(?)"); for(int j = 0; j ++; j <200){for(int i = 0; i ++; j <50){stmt.setString(1、 "feifei");}} stmt.executeupdate(); tx.commit(); // Hibernateトランザクション処理境界を使用してください...この変更には、冬眠味がします。テスト後、著者はJDBC APIをバッチ処理に使用します。これは、Hibernate APIを使用するよりもパフォーマンスがほぼ10倍高くなっています。これは間違いなくJDBCの支配的なパフォーマンスです。
hibernate2のバッチアップデートと削除では、バッチ更新操作の場合、hibernateは要件を満たすデータを見つけて、更新操作を実行します。同じことがバッチ削除にも当てはまります。最初に条件を満たすデータを見つけてから、削除操作を実行します。
これには2つの大きな欠点があります。
(1):多くの記憶を取り上げます。
(2):大規模なデータを処理する場合、更新/削除ステートメントを実行することは膨大な量であり、更新/削除ステートメントは1つのオブジェクトのみを操作できます。データベースが頻繁に動作する場合、データベースのパフォーマンスが低いと考えられます。
hibernate3がリリースされた後、バッチアップデート/削除操作のためにバルクアップデート/削除が導入されました。原則は、JDBCのバッチアップデート/削除操作に非常に似たHQLステートメントを介して、バッチ更新/削除操作を完了することです。パフォーマンスに関しては、hibernate2のバッチ更新/削除よりも大きな改善があります。
トランザクションtx = session.beginsession(); string hql = "delete sustent"; query query = session.createquery(hql); int size = query.executeupdate(); tx.commit(); ...
コンソールは、1つの削除ステートメントのみを出力します。ステートメントの実行は少なく、パフォーマンスはJDBCを使用することとほぼ同じです。パフォーマンスを向上させる良い方法です。もちろん、より良いパフォーマンスを得るために、著者はバッチの更新と削除操作がJDBCを使用することを推奨しています。方法と基本的な知識ポイントは、基本的に上記のバッチ挿入方法2と同じであるため、ここでは冗長に説明しません。
ここでは、別の方法を提供します。これは、データベース側からのパフォーマンスの向上を検討し、Hibernateプログラム側のストアドプロシージャを呼び出すことです。データベース側で実行されるストアドプロシージャは、より速く実行されます。バッチの更新を例にとると、参照コードが指定されています。
まず、データベース側にbatchupdateStudentという名前のストアドプロシージャを作成します。
生産物を作成または交換しますbatchupdateStudent(数のa)asbeginupdate学生セット年齢=年齢+1年年齢> a; end;
コールコードは次のとおりです。
トランザクションtx = session.beginsession(); connection conn = session.connection(); string pd = "…{call batchupdateStudent(?)}"; callablestatement cstmt = conn.preparecall(pd); cstmt.setint(1、20); //年齢パラメーターを20tx.commit()に設定します。上記のコードを観察すると、Hibernate APIをバイパスし、JDBC APIを使用してストアドプロシージャを呼び出し、Hibernateのトランザクション境界を使用します。ストアドプロシージャは、間違いなくバッチ処理パフォーマンスを改善する良い方法です。それらはデータベース側で直接実行され、ある程度バッチ処理の圧力をデータベースに転送します。
ポストスクリプト
この記事では、Hibernateのバッチ処理操作について説明します。出発点は、パフォーマンスの向上を検討することであり、パフォーマンスの改善のわずかな側面しか提供していません。
どの方法が採用されていても、実際の条件に基づいて考慮する必要があります。ユーザーにニーズを満たす効率的で安定したシステムを提供することが最優先事項です。
この記事が皆の冬眠プログラミングに役立つことを願っています。