更新
我已将错误提交给Adobe,并引用了这个问题
在我发现问题的真实代码中,我决定删除我对cfqueryparam的使用。我现在使用自定义函数来根据类型格式化param。我将不得不处理安全性和速度问题,但它会使当前负载下的特定过程可以接受。
将来我计划进行处理,将数据文件拉入数据库中的临时表。然后,我将对数据执行操作,并尽可能使用SQL将数据传输到实时表,而不是依赖于ColdFusion
我在插入数据时使用cfqueryparam标记循环查询时遇到问题。 (我没有使用select或update查询进行测试)。循环逐渐占用更多内存,直到请求完成后才会释放。但是,只有在函数中循环查询时才会出现此问题。
它似乎对使用的cfqueryparam标签的数量非常敏感。在这个例子中有15个值是插入,但在我的代码中,实际上需要这个工作我插入一个未知数量的值,可以使问题更严重。
以下是显示问题的代码。给它一个数据源名称(在MSSQL上测试),它将创建一个tmp表并插入记录作为示例,有或没有在函数中。内存使用情况显示在非函数循环之前,之后,然后是函数内循环之后。它还要求垃圾收集,并在输出内存信息前等待10秒,以确保它尽可能准确地显示信息。
根据我对这个特定测试的经验,函数内循环导致使用超过200mb的内存。在我的现实世界使用它崩溃ColdFusion: - (
<cfsetting enablecfoutputonly="true">
<cfsetting requesttimeout="600">
<cfset insertCount = 100000>
<cfset dsn = "TmpDB">
<cfset dropTmpTable()>
<cfset createTmpTable()>
<cfset showMemory("Before")>
<cfflush interval="1">
<cfloop from="1" to="#insertCount#" index="i">
<cfquery name="testq" datasource="#dsn#">
INSERT INTO tmp ( [col1],[col2],[col3],[col4],[col5],[col6],[col7],[col8],[col9],[col10],[col11],[col12],[col13],[col14],[col15] )
VALUES ( <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR"> )
</cfquery>
</cfloop>
<cfset showMemory("After Non-Function INSERTS")>
<cfflush interval="1">
<cfset funcTest()>
<cfset showMemory("After Function based INSERTS")>
<cfset dropTmpTable()>
<cffunction name="funcTest" output="false">
<cfset var i = 0>
<cfset var testq = "">
<cfloop from="1" to="#insertCount#" index="i">
<cfquery name="testq" datasource="#dsn#">
INSERT INTO tmp ( [col1],[col2],[col3],[col4],[col5],[col6],[col7],[col8],[col9],[col10],[col11],[col12],[col13],[col14],[col15] )
VALUES ( <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR">, <cfqueryparam value="TestValue" cfsqltype="CF_SQL_CHAR"> )
</cfquery>
</cfloop>
</cffunction>
<cffunction name="showMemory" output="true">
<cfargument name="label" required="true">
<cfset var runtime = "">
<cfset var memoryUsed = "">
<cfset requestGC("10")>
<cfset runtime = CreateObject("java","java.lang.Runtime").getRuntime()>
<cfset memoryUsed = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024>
<cfoutput>
<h2>#arguments.label#</h2>
Memory Used: #Round(memoryUsed)#mb
</cfoutput>
</cffunction>
<cffunction name="requestGC">
<cfargument name="waitSeconds" required="false" default="0" type="numeric">
<cfscript>
createObject("java","java.lang.Runtime").getRuntime().gc();
createObject("java", "java.lang.Thread").sleep(arguments.waitSeconds*1000);
</cfscript>
</cffunction>
<cffunction name="dropTmpTable" output="false">
<cftry>
<cfquery datasource="#dsn#">
DROP TABLE tmp
</cfquery>
<cfcatch type="database"></cfcatch>
</cftry>
</cffunction>
<cffunction name="createTmpTable" output="false">
<cfquery datasource="#dsn#">
CREATE TABLE tmp(
col1 nchar(10) NULL, col2 nchar(10) NULL, col3 nchar(10) NULL, col4 nchar(10) NULL, col5 nchar(10) NULL, col6 nchar(10) NULL, col7 nchar(10) NULL, col8 nchar(10) NULL, col9 nchar(10) NULL, col10 nchar(10) NULL, col11 nchar(10) NULL, col12 nchar(10) NULL, col13 nchar(10) NULL, col14 nchar(10) NULL, col15 nchar(10) NULL
) ON [PRIMARY]
</cfquery>
</cffunction>
为了表明在操作期间可以释放内存,下面是构建更大结构的示例代码,并显示在覆盖变量和垃圾收集之前和之后使用的内存。在人口为118mb之后使用的这个内存中,在覆盖和垃圾收集之后它是31mb。
<cfset showMemory("Before struct creation")>
<cfflush interval="1">
<cfset tmpStruct = {}>
<cfloop from="1" to="1000000" index="i">
<cfset tmpStruct["index:#i#"] = "testvalue testvalue testvalue testvalue testvalue testvalue testvalue testvalue testvalue testvalue">
</cfloop>
<cfset showMemory("After struct population")>
<cfflush interval="1">
<cfset tmpStruct = {}>
<cfset showMemory("After struct overwritten")>
答案 0 :(得分:4)
您是否在管理员中进行了调试?
如果是这样,即使您有showdebugoutput="false"
,CF也会保留有关所有这些查询的调试信息,并且通过这么多查询,调试信息可能会很快建立起来。
此外,如果你真的有80,000行要插入,你可能想要以不同的方式这样做 - 例如生成直接针对数据库运行的导入脚本(没有CF / JDBC阻碍)。
答案 1 :(得分:3)
也许multiple insert可以提供帮助?这种技术本身通常工作得更快,节省一些时间可以帮助您节省一些内存。
是的我看过你的笔记“插入一个未知数量的值”,但是如果你在一个insterting批次中有一定数量的字段/值,这应该有效。
答案 2 :(得分:2)
不知道它是否有所作为,但要尝试一下 - 缩小函数内循环,并多次循环函数。
这对记忆的作用可能有助于缩小它用完的地方
<cffunction name="funcTest" output="false">
<cfargument name="from" />
<cfargument name="to" />
<cfset var i = 0>
<cfset var testq = "">
<cfloop from="#arguments.from#" to="#arguments.to#" index="i">
<cfquery name="testq" datasource="#dsn#">
...
</cfquery>
</cfloop>
</cffunction>
<cfset BlockSize = 100 />
<cfloop index="CurBlock" from="1" to="#(InsertCount/BlockSize)#">
<cfset funcTest
( from : CurBlock*(BlockSize-1) + 1
, to : CurBlock*BlockSize
)/>
</cfloop>
答案 3 :(得分:2)
我遇到了类似的问题。
http://misterdai.wordpress.com/2009/06/24/when-not-to-use-cfqueryparam/
这种方法取决于少数几件事。如果您可以信任这些数据,请不要使用cfqueryparam,这将大大减少内存使用量。从那里,尽可能地减少SQL。我每行做了相当多的数据库工作,所以我创建了一个存储过程。打击内存使用的最大好处是缓冲对数据库的SQL调用。创建一个数组,将SQL附加到它,然后每50行(测试后的个人选择)在CfQuery标记内的数组上执行ArrayToList。这将数据库流量限制为更小,但更大,而不是更小的数据流。
毕竟,事情对我有用。但我仍然认为ColdFusion真的不适合这种类型的任务,如果可能的话,更多的是数据库服务器本身的域。
答案 4 :(得分:1)
我的第一个猜测是在cfqueryparam中键入值 - 如type =“CF_SQL_CHAR”。为什么这有帮助?我不确定,但我可以猜测,非类型变量会产生额外的开销。
答案 5 :(得分:1)
假设您正在使用CF8 ......不确定CF7中是否会发生这种情况......
尝试在数据源“高级设置”中关闭“Max Pooled Statements”(将其设置为零)...我打赌你的内存泄漏就会消失......
这就是我发现错误的地方......这导致某些CF服务器上的各种崩溃,直到我们发现这一点......我们现在因此而稳定100%......
Patrick Steil
答案 6 :(得分:0)
尝试添加“变量”。在每个查询之前你的cffunctions。我有一个类似的问题,并修复了它。
所以改变:
<cfquery name="testq" datasource="CongressPlus">
到
<cfquery name="variables.testq" datasource="CongressPlus">
干杯,
托马斯
答案 7 :(得分:0)
整个社区都充分记录了CF在请求完成之前不会释放内存。即使直接调用GC也不会影响在运行请求期间释放内存。不知道这是设计还是错误。
我不知道为什么你甚至想要在CF中做这样的事情。无论您使用哪种数据库引擎,都没有理由使用CF将80K行插入数据库。
现在,如果您有理由需要这样做,例如您从上传的CSV或XML文件中获取数据; MSSQL有更好的方法可以做到这一点和解决方法。
我多年来采用的一种方法是在MSSQL中创建一个存储过程,调用BCP或BULK INSERT来读取包含要插入的数据的文件。
这种方法最好的事情是CF唯一要做的就是处理文件上传,而MMSQL正在处理文件的所有工作。 MSSQL使用BCP或BULK INSERT插入数百万行没有问题,并且 INFINITELY 比CF可以处理的任何内容都快。
答案 8 :(得分:0)
在大量查询循环中防止cfqueryparam内存泄漏的方法是不使用cfqueryparam。然而,更广泛的答案是避免CF的低效率和内存泄漏是在这些情况下不使用CF.我在当时将特定进程设置为可接受的负载级别,但从长远来看,将使用另一种语言重写它,可能直接在数据库引擎中重写。
答案 9 :(得分:0)
我不知道这是否能解决您的问题,但是当我有多个这样的插入时我通常会做的是,SQL语句本身的循环而不是整个cfquery。
所以不要:
<cfloop from="1" to="#insertCount#" index="i">
<cfquery name="testq" datasource="#dsn#">
...
</cfquery>
</cfloop>
我做:
<cfquery name="testq" datasource="#dsn#">
<cfloop from="1" to="#insertCount#" index="i">
...
</cfloop>
</cfquery>
因此,您不必多次调用数据库,而只需要一个大数据库。
我不知道这会如何影响你的内存泄漏问题,但我从来没有遇到任何内存泄漏这样做。