グレイルの大規模なデータセットのパフォーマンスを改善するのに役立つ必要があります
-
03-10-2019 - |
質問
このソリューションは機能しますが、パフォーマンスは予想よりも低くなっています。 200k列を返すクエリには数分かかり、開発ボックスにCPUをペグします。クエリアナライザーで同じ*クエリを実行すると、すべての結果が1分未満で返されます。
Class MyController {
def index = {...}
...
def csv = {
...
def rs = DomainClass.createCritera().scroll {}
while(rs.next()){
response.getOutputStream().print(rs.getString(1)\n)
}
...
}
DB = SQL Server 2005 DEVマシンとは別の専用ボックス上のサーバー。
また、SQL Serverプロファイラーを介して、Gorm/HibernateがSP_CURSORPREPEXECとSP_CURSORFETCHを使用して、一度に128行の結果を読み取っていることに気付きました。カーソルがオプションである場合は、カーソルを使用しないようにしたいと思います。
それが問題なのかどうかはわかりませんが、助けることしかできません。冬眠では、スクロールをフォワードのみに設定することができますが、洗浄の同様の設定を見つけるのに苦労しています。
オリジナルの冬眠 問題.
解決策:冬眠をバイパスします。 10分から15秒。
Class MyController {
def DataSource
def index = {...}
...
def csv = {
...
def out = response.getOutoutStream()
Sql sql = new Sql(dataSource)
sql.eachRow("select c1, c2 from t1",{
out.println( it.c1 + "," + it.c2 )
})
...
}
*同じ= SQL Server Profilerからカットおよび貼り付けをしますが、ラッピングSP_CURSORPREPEXEC SPROCを除く。
解決
Hibernateは実際にはバッチロード用に作られたものではありませんが、試してみることができることがいくつかあります(そのほとんどは、Scrollableresultの使用を削除し、オブジェクトの結果を伴う定期的なクエリを実行する必要があります)。
- Hibernate/Gormをバイパスして、必要な場所の一握りの場所に直接SQLに移動します。ええ、私は知っていますが、さらに悪いことになると...
- session.setreadonly()またはquery.setreadonly()を呼び出して、hibernate'sを無効にします ステートスナップショット
Hibernateのステートレスセッションを試してみてください。あなたがしているのが読んでいるだけなら、これは正常に動作するかもしれません。ステートレスセッションは、通常の冬眠セッションよりもはるかに低いオーバーヘッドを持っていますが、すべてのキャッシュとオブジェクトの状態追跡を放棄します。あなたはそれを使用するためにこのようなことをする必要があります:
def Session statelessSession = sessionFactory.openStatelessSession() statelessSession.beginTransaction() // ... statelessSession.getTransaction().commit() statelessSession.close()
セッションを25または50のバッチでフラッシュします。本質的に、持ち帰ったアイテムを繰り返しているときに、SESSION.FLUSH()を実行します。そうしないと、メモリがなくなり、ゴミコレクターが夢中になり始めるまで、セッションは成長し続けます。これが、プロセッサがペグされている理由かもしれません。
幸運を!
他のヒント
何かがGormにサポートされていない場合、直接冬眠するためにドロップダウンするのは簡単です:
import org.hibernate.ScrollMode
class MyController {
def index = {...}
def csv = {
DomainClass.withSession { session ->
def rs = session.createCriteria(DomainClass).scroll(ScrollMode.FORWARD_ONLY)
while (rs.next()) {
response.outputStream.print rs.getString(1)
}
}
}
}
HQLクエリを使用して同じことをすることができます session.createQuery(...)
代わりは。
Grails基準とScrollModeを使用する別の方法:
Criteria criteria = Domain.createCriteria().buildCriteria{
eq('id', id)
}
ScrollableResults results = criteria.scroll(ScrollMode.FORWARD_ONLY)
int i = 0
while (results.next()){
...
if (++i % 50 == 0){
Domain.withSession { Session session ->
session.flush()
session.clear()
}
}
}
注目に値するいくつかのこと:
- コントローラーは、各コールの新しいDBセッションとトランザクションを開く可能性があるため、クエリを実行しないでください。
- いくつかの列のみが必要な場合は、基準で投影を使用できます。
- Hibernateが発射しているSQLを記録するのに役立つかもしれません。これまで、 hibernateは、たとえあなたがそれを使用していなくても、すべてのオブジェクトの無意味な1対1の関係を怠る. 。ただし、予測を使用している場合は心配する必要はありません。
バッチインサートを使用すると、ゴームクリーンアップメソッドとステートレスセッション方法よりも速くなります。Belowの例は、Grailsにバッチインサートを実装する方法を支援します。
Date startTime = new Date()
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
(1..50000).each {counter ->
Person person = new Person()
person.firstName = "abc"
person.middleName = "abc"
person.lastName = "abc"
person.address = "abc"
person.favouriteGame = "abc"
person.favouriteActor = "abc"
session.save(person)
if(counter.mod(100)==0) {
session.flush();
session.clear();
}
if(counter.mod(10000)==0) {
Date endTime =new Date()
println "Total record insert Counter =>"+counter+" Time =>"+TimeCategory.minus(endTime,startTime)
}
}
tx.commit();
session.close();