减少退出服务方法回到控制器方法的处理时间
Reduce processing time of exiting a Service method back to the Controller method
在我的 Grails 应用程序中,我在一次保存调用期间创建了很多个人 DomainObject
link:
import grails.converters.JSON
class SaveController {
def saveService
def save() {
println (new Date()) + " Enter SaveController.save()."
Map status = saveService.save(params)
println (new Date()) + " Finished calling saveService.save()."
render status as JSON
}
}
link 调用服务将其每个内容保存到数据库:
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
class SaveService {
Map save(GrailsParameterMap params) {
List<DomainObject> dos = new ArrayList()
println (new Date()) + " Iterate list and create DomainObject per element."
params.list.each { l ->
dos.push(new DomainObject(l))
}
println (new Date()) + " Started saving all DomainObjects."
dos.each { d ->
d.save()
}
println (new Date()) + " Finished saving all ${dos.size()} DomainObjects."
return [done: true]
}
}
问题是它在过程的某个部分阻塞,从我放置的打印件中可以看出:
Fri Jun 01 21:47:55 SGT 2018 Enter SaveController.save().
Fri Jun 01 21:47:55 SGT 2018 Iterate list and create DomainObject per element.
Fri Jun 01 21:47:55 SGT 2018 Started saving all DomainObject.
Fri Jun 01 21:48:13 SGT 2018 Finished saving all 4316 DomainObject.
// Waits from some minutes before reaching the next println.
Fri Jun 01 21:52:02 SGT 2018 Finished calling saveService.save().
我的猜测是,每次 .save()
调用数据库都需要时间 persisting/committing。我的问题是,有什么方法可以修改代码,使其将 List<DomainObject> dos
的所有元素作为一个整体提交一次,从而消除退出 saveService.save()
.[= 之前所需的长时间22=]
我曾尝试将 d.save()
更改为 d.save(flush: true)
,但速度保持不变。我正在寻找一个 groovy Closure
,它会在完成后自动提交其中的事务,就像这样(我不知道是否有这样的实际 Closure
):
commitOnceDone { session ->
dos.each { d ->
d.save()
}
}
// Once reached here, will fire a single COMMIT; that will affect
// all transactions that happened inside the Closure
不确定您使用的是哪个版本的 Grails,但从 3.3.5 开始,Grails 通过域对象上的 withTransaction
方法或域对象上的 @Transactional
注释为您提供细粒度的事务控制服务 class 在 class 或方法级别。
有关详细信息,请查看 section on declarative transactions in the docs。
您需要批量插入。您需要做两件事:
在 Hibernate/JDBC 级别启用批处理:在 application.yml 中,设置 hibernate.jdbc.batch_size: 50
不要让数据库生成主键,否则无法批量插入。查看主键生成策略:http://docs.grails.org/3.1.1/ref/Database%20Mapping/id.html and http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-declaration-id-generator
对于非常简单的使用/测试,您可以在 application.groovy 中使用它:
grails.gorm.default.mapping = {
'id'(generator: 'increment') // The application instance will generate the ID, so it cannot be clustered
}
Fri Jun 01 21:48:13 SGT 2018 Finished saving all 4316 DomainObject.
请注意,在您打印此内容时,由于您尚未刷新 Hibernate 会话,因此数据库中未保存任何内容。要进行批处理,请不要将 'flush: true' 放入 .save() 调用中,这样如果您将应用程序设置为批处理,Hibernate 将对插入进行批处理。
还有其他可能的优化,但这个应该是最大的一个。
在我的 Grails 应用程序中,我在一次保存调用期间创建了很多个人 DomainObject
link:
import grails.converters.JSON
class SaveController {
def saveService
def save() {
println (new Date()) + " Enter SaveController.save()."
Map status = saveService.save(params)
println (new Date()) + " Finished calling saveService.save()."
render status as JSON
}
}
link 调用服务将其每个内容保存到数据库:
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
class SaveService {
Map save(GrailsParameterMap params) {
List<DomainObject> dos = new ArrayList()
println (new Date()) + " Iterate list and create DomainObject per element."
params.list.each { l ->
dos.push(new DomainObject(l))
}
println (new Date()) + " Started saving all DomainObjects."
dos.each { d ->
d.save()
}
println (new Date()) + " Finished saving all ${dos.size()} DomainObjects."
return [done: true]
}
}
问题是它在过程的某个部分阻塞,从我放置的打印件中可以看出:
Fri Jun 01 21:47:55 SGT 2018 Enter SaveController.save().
Fri Jun 01 21:47:55 SGT 2018 Iterate list and create DomainObject per element.
Fri Jun 01 21:47:55 SGT 2018 Started saving all DomainObject.
Fri Jun 01 21:48:13 SGT 2018 Finished saving all 4316 DomainObject.
// Waits from some minutes before reaching the next println.
Fri Jun 01 21:52:02 SGT 2018 Finished calling saveService.save().
我的猜测是,每次 .save()
调用数据库都需要时间 persisting/committing。我的问题是,有什么方法可以修改代码,使其将 List<DomainObject> dos
的所有元素作为一个整体提交一次,从而消除退出 saveService.save()
.[= 之前所需的长时间22=]
我曾尝试将 d.save()
更改为 d.save(flush: true)
,但速度保持不变。我正在寻找一个 groovy Closure
,它会在完成后自动提交其中的事务,就像这样(我不知道是否有这样的实际 Closure
):
commitOnceDone { session ->
dos.each { d ->
d.save()
}
}
// Once reached here, will fire a single COMMIT; that will affect
// all transactions that happened inside the Closure
不确定您使用的是哪个版本的 Grails,但从 3.3.5 开始,Grails 通过域对象上的 withTransaction
方法或域对象上的 @Transactional
注释为您提供细粒度的事务控制服务 class 在 class 或方法级别。
有关详细信息,请查看 section on declarative transactions in the docs。
您需要批量插入。您需要做两件事:
在 Hibernate/JDBC 级别启用批处理:在 application.yml 中,设置 hibernate.jdbc.batch_size: 50
不要让数据库生成主键,否则无法批量插入。查看主键生成策略:http://docs.grails.org/3.1.1/ref/Database%20Mapping/id.html and http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#mapping-declaration-id-generator
对于非常简单的使用/测试,您可以在 application.groovy 中使用它:
grails.gorm.default.mapping = {
'id'(generator: 'increment') // The application instance will generate the ID, so it cannot be clustered
}
Fri Jun 01 21:48:13 SGT 2018 Finished saving all 4316 DomainObject.
请注意,在您打印此内容时,由于您尚未刷新 Hibernate 会话,因此数据库中未保存任何内容。要进行批处理,请不要将 'flush: true' 放入 .save() 调用中,这样如果您将应用程序设置为批处理,Hibernate 将对插入进行批处理。
还有其他可能的优化,但这个应该是最大的一个。