4

更新:

  • 我已将错误提交给 Adob​​e 并引用了这个 SO question

  • 在发生问题的实际代码中,我决定只删除对 cfqueryparam 的使用。我现在使用自定义函数来根据类型格式化参数。我将不得不处理安全和速度方面的问题,但它可以让特定进程在当前负载下可接受地工作。

  • 将来,我计划将数据文件拉入数据库中的临时表中。然后我将尽可能使用 SQL 对数据执行操作并将数据传输到实时表,而不是依赖 ColdFusion


我在插入数据时使用 cfqueryparam 标记循环查询时遇到问题。(我没有测试过选择或更新查询)。循环逐渐占用更多内存,直到请求完成才释放。但是,仅当在函数中循环查询时才会出现此问题。

它似乎对使用的 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")>
4

10 回答 10

4

你在Administrator中有调试吗?

如果是这样,即使你有showdebugoutput="false",CF 也会保留所有这些查询的调试信息,并且有这么多查询,调试信息可以快速建立。


此外,如果您确实有 80,000 行要插入,您可能希望以不同的方式执行此操作 - 例如生成直接针对 DB 运行的导入脚本(没有 CF/JDBC 妨碍)。

于 2009-05-12T22:43:01.210 回答
3

也许多个插入可以帮助?这种技术本身通常工作得更快,节省一些时间可以帮助您节省一些内存。

是的,我已经看到您的注释“插入未知数量的值”,但是如果您在单个插入批次中有恒定数量的字段/值,这应该可以工作。

于 2009-05-13T07:09:48.823 回答
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>
于 2009-05-12T23:14:18.943 回答
2

我遇到了类似的问题。

http://misterdai.wordpress.com/2009/06/24/when-not-to-use-cfqueryparam/

方法取决于几件事。如果您可以信任数据,请不要使用 cfqueryparam,这会大大减少内存使用量。从那里,尽可能减少 SQL。我每行都在做相当多的数据库工作,所以我创建了一个存储过程。对抗内存使用的最大好处是缓冲对数据库的 SQL 调用。创建一个数组,将您的 SQL 附加到它,然后每 50 行(测试后的个人选择)在 CfQuery 标记内的数组上执行一个 ArrayToList。这将数据库流量限制为更少但更大,而不是许多较小的流量。

毕竟,事情对我有用。但我仍然认为 ColdFusion 确实无法胜任这类任务,如果可能的话,更多的是数据库服务器本身的域。

于 2011-01-26T16:42:51.270 回答
1

我的第一个猜测是在您的 cfqueryparam 中输入值 - 如 type="CF_SQL_CHAR"。为什么会有帮助?我不确定,但我可以猜测非类型变量会产生额外的开销。

于 2009-05-13T13:21:56.623 回答
1

假设您使用的是 CF8……不确定这是否发生在 CF7 中……

尝试在您的数据源“高级设置”中关闭“Max Pooled Statements”(将其设置为零)......我敢打赌你的内存泄漏会消失......

这就是我发现错误的地方......这导致一些CF服务器上的各种崩溃,直到我们发现这个......我们现在稳定了100%......因为这个......

帕特里克·斯泰尔

于 2009-05-14T02:40:57.437 回答
0

尝试添加“变量”。在您的 cffunctions 中的每个查询之前。我有一个类似的问题,这解决了它。

所以改变:

<cfquery name="testq" datasource="CongressPlus">

<cfquery name="variables.testq" datasource="CongressPlus">

干杯,

托马斯

于 2009-05-13T12:46:23.967 回答
0

整个社区都有很好的记录,CF 在请求完成之前不会释放内存。即使直接调用 GC 也不会影响在运行请求期间释放内存。不知道这是设计使然还是错误。

我不知道为什么你甚至想在 CF 中做这样的事情。无论您使用哪种数据库引擎,都没有理由使用 CF 将 80K 行插入数据库。

现在,如果您有需要这样做的原因,例如您从上传的 CSV 或 XML 文件中获取数据;MSSQL 有很多更好的方法和解决方法。

多年来我采用的一种方法是在 MSSQL 中创建一个存储过程,该过程调用BCPBULK INSERT来读取包含要插入的数据的文件。

这种方法最好的一点是,CF 唯一要做的就是处理文件上传,而 MMSQL 正在处理文件的所有工作。MSSQL 使用 BCP 或 BULK INSERT 插入数百万行没有问题,并且将无限快于 CF 可以处理的任何内容。

于 2009-05-13T14:16:32.650 回答
0

在大型查询循环中防止 cfqueryparam 内存泄漏的方法是不使用 cfqueryparam。然而,更广泛的答案是避免 CF 的低效率和内存泄漏是在这些情况下不使用 CF。我当时将特定过程的负载达到了可接受的水平,但从长远来看,我会用另一种语言重写它,可能是直接在数据库引擎中的 C#。

于 2010-11-06T17:50:50.500 回答
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>

因此,您只有一个大的,而不是多次调用数据库。

我不知道这会如何影响您的内存泄漏问题,但我从未经历过任何内存泄漏。

于 2011-12-02T17:14:30.440 回答