此解决方案有效,但性能低于预期。返回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服务器。
我还注意到,通过SQL Server Profiler,gorm / hibernate使用sp_cursorprepexec和sp_cursorfetch一次读取结果128行。如果它是一个选项,我想尝试不使用光标。
不确定是否是问题,但只能提供帮助。在休眠时,可以将滚动设置为仅向前但我无法找到类似的grails设置。
原始的休眠issue。
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 )
})
...
}
* same =从SQL Server Profiler剪切和粘贴,但不包括sp_cursorprepexec sproc。
答案 0 :(得分:4)
如果GORM不支持某些内容,可以直接下载到Hibernate:
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)
}
}
}
}
您可以使用session.createQuery(...)
代替HQL查询。
答案 1 :(得分:3)
Hibernate并不是真正用于批处理加载,但有一些事情你可以尝试(其中大部分都要求你放弃ScrollableResult用法,只是用对象结果进行常规查询)。
试试Hibernate的无状态会话。如果您所做的只是阅读,这可能会正常。无状态会话的开销远低于常规Hibernate会话,但您将放弃所有缓存和对象状态跟踪。你必须做这样的事情才能使用它:
def Session statelessSession = sessionFactory.openStatelessSession() statelessSession.beginTransaction() // ... statelessSession.getTransaction().commit() statelessSession.close()
以25或50的批量刷新会话。实际上,当您迭代已经带回的项目时,请执行session.flush()。如果你不这样做,会话将继续增长,直到你的内存不足,你的垃圾收集器开始变得疯狂。这可能就是您的处理器被挂钩的原因。
答案 2 :(得分:0)
使用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()
}
}
}
答案 3 :(得分:0)
值得注意的一些事情:
答案 4 :(得分:0)
使用批量插入,它比gorm清理方法和无状态会话方法更快。下面的例子可以帮助您实现如何在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();