我在使用 CFWheels 将大量记录保存到数据库时遇到问题。这是一个例子:
<cfloop from="1" to="10000" index="i">
<cfset var newUser = model("user").new()>
<cfset newUser.name = "Test"&i>
<cfset newUser.save()>
</cfloop>
这会导致 java.lang.OutOfMemoryError
请帮助我如何解决这个问题。
我在使用 CFWheels 将大量记录保存到数据库时遇到问题。这是一个例子:
<cfloop from="1" to="10000" index="i">
<cfset var newUser = model("user").new()>
<cfset newUser.name = "Test"&i>
<cfset newUser.save()>
</cfloop>
这会导致 java.lang.OutOfMemoryError
请帮助我如何解决这个问题。
循环导致 OOM 的多个数据库调用是一个已知的 ColdFusion 错误。幸运的是,有一个解决方法,使用<cfthread/>
. 您应该能够像这样更改您的代码:
<cfloop from="1" to="10000" index="i">
<cfset threadName = "thread" & createUuid()>
<cfthread name="#threadName#">
<cfset var newUser = model("user").new()>
<cfset newUser.name = "Test"&i>
<cfset newUser.save()>
</cfthread>
<cfthread action="join" name="#threadName#">
</cfloop>
在这种情况下,您使用线程只是为了它的副作用,在不同的上下文中运行,这样它就不会保留在堆上。因此,在声明线程后立即加入,因此它实际上并没有并行运行任何东西。
这里发生了一些相当低效的事情。首先,它会生成 1,000 个user
对象,这在 ColdFusion 中的单个请求中并不是一个好主意。其次,它运行 1,000 个数据库查询,这在任何编程语言中都不是一个好主意。
在这种情况下,我会停止使用模型对象,并弄清楚如何将逻辑压缩到单个数据库查询中。Wheels 中的 ORM 东西通常非常有用,但在这种情况下它有其局限性。
例如,如果您使用的是 SQL Server 2008,则可以在user
模型中执行此操作,以使所有内容都在一次调用下cfquery
:
<cffunction name="batchCreate">
<cfquery datasource="#get('dataSourceName')#">
INSERT INTO
#this.tableName()# (#this.columnNameForProperty("name")#)
VALUES
<cfloop from="1" to="10000" index="i">
(<cfqueryparam cfsqltype="cf_sql_varchar" value="Test#i#">)
<cfif i lt 10000>,</cfif>
</cfloop>
</cfquery>
</cffunction>
当然,如果您使用 MySQL 或其他数据库引擎,查询看起来会有所不同。