Coldfusion 10 中可重现的 CFQUERYPARAM 性能问题
Reproducible CFQUERYPARAM performance issue in Coldfusion 10
我一直能够通过查询 SQL Server 2008 R2 的 Coldfusion 10 重现严重的参数化性能问题,并且很想知道其他人得到了什么。代码如下。
测试是做什么的?
它创建了一个包含 100 行的 table。一个数据列除了一个之外都是空白的。然后它 运行 进行了 10 次 Coldfusion 查询,一半使用 cfqueryparam,另一半使用一个简单的字符串。它 returns 一个包含每个响应时间的列表。当我 运行 这样做时,除了初始调用之外,参数化查询 运行 慢得多(大约 10-100 倍)。
SQL 服务器发生了什么?
我看不出 SQL 服务器有什么不同。在这两种情况下,计划缓存指示几乎相同的计划(一个显然是参数化的)并且分析器显示对两者的快速响应。但是,Coldfusion 在参数化查询方面遇到了困难。
什么解决了这个问题?
奇怪的是,如果我将 varchar 更改为 nvarchar,问题就会消失。或者,如果我将非空白移动到开头,那么两个响应都很慢(去图)。如果我将所有记录设为空白或非空白,那么问题就不存在了。它必须是混合的。我无法在 CF9 中重现该问题,但还没有尝试过 CF11。
<cfset datasource="yourdatasource" />
<cfquery name="createdata" datasource="#datasource#">
--EMPTY PREVIOUS TESTS
IF OBJECT_ID('aaatest', 'U') IS NOT NULL
BEGIN
TRUNCATE TABLE aaatest;
DROP TABLE aaatest;
END
--CREATE TABLE TO CONTAIN DATA
CREATE TABLE [dbo].[aaatest](
[id] [int] NOT NULL,
[somedata] [varchar](max) NULL,
[somekey] [int] NOT NULL
) ON [PRIMARY];
--INSERT 100 ROWS WITH 99 BLANK AND 1 NON-BLANK
WITH datatable AS (
SELECT 1 id
UNION all
SELECT id + 1
FROM datatable
WHERE id + 1 <= 100
)
INSERT INTO aaatest(id,somekey,somedata)
SELECT id,1,case when id=99 then 'A' else '' end
FROM datatable;
</cfquery>
<cfset results=[] />
<cfloop from="1" to="10" index="n">
<!--- use parameters for every other test --->
<cfset useParameters = (n mod 2 is 0) />
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey=
<cfif useParameters>
<cfqueryparam value="1" CFSQLType="CF_SQL_INTEGER" />
<cfelse>
1
</cfif>
</cfquery>
<!--- store results with parameter test marked with a P --->
<cfset arrayAppend(results,(useParameters?'P':'')&result.executiontime) />
</cfloop>
<cfdump var="#results#" />
回答——@Raspin 在评论中确认,设置 NOCOUNT ON
解决了问题。
原始建议:
这可能是一个线索。您不是在处理 INDEX,但我的想法是 SQL 必须进行数据转换。我不认为这么少的行会很重要,但我也不认为你会遇到这个问题:
Slow query with cfqueryparam searching on indexed column containing hashes
What might be happening is there is a setting in ColdFusion administrator if cfqueryparam sends varchars as unicode or not. If that setting does not match the column setting (in your case, if that setting is enabled) then MS SQL will not use that index.
我建议尝试的另一件事是将整个 SELECT
语句包装在 IF
语句中。我的想法是,它可能以 SQL 认为它无法重用查询计划的方式出现。这意味着你的性能损失实际上是重新编译:
<cfloop from="1" to="10" index="n">
<cfset useParameters = (n mod 2 is 0) />
<cfif useParameters>
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey= <cfqueryparam value="1" CFSQLType="CF_SQL_INTEGER" />
</cfquery>
<cfelse>
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey= 1
</cfquery>
</cfif>
<cfset arrayAppend(results,(useParameters?'P':'')&result.executiontime) />
</cfloop>
我一直能够通过查询 SQL Server 2008 R2 的 Coldfusion 10 重现严重的参数化性能问题,并且很想知道其他人得到了什么。代码如下。
测试是做什么的? 它创建了一个包含 100 行的 table。一个数据列除了一个之外都是空白的。然后它 运行 进行了 10 次 Coldfusion 查询,一半使用 cfqueryparam,另一半使用一个简单的字符串。它 returns 一个包含每个响应时间的列表。当我 运行 这样做时,除了初始调用之外,参数化查询 运行 慢得多(大约 10-100 倍)。
SQL 服务器发生了什么? 我看不出 SQL 服务器有什么不同。在这两种情况下,计划缓存指示几乎相同的计划(一个显然是参数化的)并且分析器显示对两者的快速响应。但是,Coldfusion 在参数化查询方面遇到了困难。
什么解决了这个问题? 奇怪的是,如果我将 varchar 更改为 nvarchar,问题就会消失。或者,如果我将非空白移动到开头,那么两个响应都很慢(去图)。如果我将所有记录设为空白或非空白,那么问题就不存在了。它必须是混合的。我无法在 CF9 中重现该问题,但还没有尝试过 CF11。
<cfset datasource="yourdatasource" />
<cfquery name="createdata" datasource="#datasource#">
--EMPTY PREVIOUS TESTS
IF OBJECT_ID('aaatest', 'U') IS NOT NULL
BEGIN
TRUNCATE TABLE aaatest;
DROP TABLE aaatest;
END
--CREATE TABLE TO CONTAIN DATA
CREATE TABLE [dbo].[aaatest](
[id] [int] NOT NULL,
[somedata] [varchar](max) NULL,
[somekey] [int] NOT NULL
) ON [PRIMARY];
--INSERT 100 ROWS WITH 99 BLANK AND 1 NON-BLANK
WITH datatable AS (
SELECT 1 id
UNION all
SELECT id + 1
FROM datatable
WHERE id + 1 <= 100
)
INSERT INTO aaatest(id,somekey,somedata)
SELECT id,1,case when id=99 then 'A' else '' end
FROM datatable;
</cfquery>
<cfset results=[] />
<cfloop from="1" to="10" index="n">
<!--- use parameters for every other test --->
<cfset useParameters = (n mod 2 is 0) />
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey=
<cfif useParameters>
<cfqueryparam value="1" CFSQLType="CF_SQL_INTEGER" />
<cfelse>
1
</cfif>
</cfquery>
<!--- store results with parameter test marked with a P --->
<cfset arrayAppend(results,(useParameters?'P':'')&result.executiontime) />
</cfloop>
<cfdump var="#results#" />
回答——@Raspin 在评论中确认,设置 NOCOUNT ON
解决了问题。
原始建议:
这可能是一个线索。您不是在处理 INDEX,但我的想法是 SQL 必须进行数据转换。我不认为这么少的行会很重要,但我也不认为你会遇到这个问题:
Slow query with cfqueryparam searching on indexed column containing hashes
What might be happening is there is a setting in ColdFusion administrator if cfqueryparam sends varchars as unicode or not. If that setting does not match the column setting (in your case, if that setting is enabled) then MS SQL will not use that index.
我建议尝试的另一件事是将整个 SELECT
语句包装在 IF
语句中。我的想法是,它可能以 SQL 认为它无法重用查询计划的方式出现。这意味着你的性能损失实际上是重新编译:
<cfloop from="1" to="10" index="n">
<cfset useParameters = (n mod 2 is 0) />
<cfif useParameters>
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey= <cfqueryparam value="1" CFSQLType="CF_SQL_INTEGER" />
</cfquery>
<cfelse>
<cfquery name="myquery" datasource="#datasource#" result="result">
SELECT somedata
FROM aaatest
WHERE somekey= 1
</cfquery>
</cfif>
<cfset arrayAppend(results,(useParameters?'P':'')&result.executiontime) />
</cfloop>