从.NET应用程序到SQL Server的偶尔缓慢调用

时间:2008-09-11 21:41:11

标签: sql-server-2005 data-retrieval

我在SQL Server中有一个表,我继承了仍在生产中的遗留系统,该系统是根据下面的代码构建的。我创建了一个SP来查询表,如下面的表创建语句中的代码所述。我的问题是,偶尔通过Enterprise Library 4和DataReader对象从.NET调用此SP的速度很慢。 SP通过数据层中的循环结构调用,该循环结构指定进入SP以便填充用户对象的参数。同样重要的是要提到在循环结构的每次传递中都不会发生慢速调用。它通常可以在一天或更长时间内完成,然后开始呈现,这使得调试非常困难。

该表包含约500万行。例如,缓慢的呼叫将花费长达10秒的时间,而快速的呼叫平均需要0到10毫秒。我在慢速调用期间检查了锁定/阻塞事务,没有找到。我在数据层中创建了一些自定义性能计数器来监控呼叫时间。从本质上讲,当性能不好时,对于那次通话来说真的很糟糕。但是当它很好的时候,它真的很棒。我已经能够在几个不同的开发人员机器上重新创建这个问题,但不能在我们的开发和登台数据库服务器上重新创建,这当然拥有更强大的硬件。通常,通过重新启动SQL Server服务来解决问题,但并非总是如此。表格中有我正在查询的字段的索引,但索引比我想要的多。但是,由于它可能对遗留系统产生影响,我对删除任何带有索引的玩具犹豫不决。以前有没有人遇到这样的问题,或者你有建议补救它吗?

CREATE TABLE [dbo].[product_performance_quarterly](
    [performance_id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [product_id] [int] NULL,
    [month] [int] NULL,
    [year] [int] NULL,
    [performance] [decimal](18, 6) NULL,
    [gross_or_net] [char](15) NULL,
    [vehicle_type] [char](30) NULL,
    [quarterly_or_monthly] [char](1) NULL,
    [stamp] [datetime] NULL CONSTRAINT [DF_product_performance_quarterly_stamp]  DEFAULT (getdate()),
    [eA_loaded] [nchar](10) NULL,
    [vehicle_type_id] [int] NULL,
    [yearmonth] [char](6) NULL,
    [gross_or_net_id] [tinyint] NULL,
 CONSTRAINT [PK_product_performance_quarterly_4_19_04] PRIMARY KEY CLUSTERED 
(
    [performance_id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[product_performance_quarterly]  WITH NOCHECK ADD  CONSTRAINT [FK_product_performance_quarterlyProduct_id] FOREIGN KEY([product_id])
REFERENCES [dbo].[products] ([product_id])
GO
ALTER TABLE [dbo].[product_performance_quarterly] CHECK CONSTRAINT [FK_product_performance_quarterlyProduct_id]

CREATE PROCEDURE [eA.Analytics.Calculations].[USP.GetCalculationData]
(
    @PRODUCTID INT,                     --products.product_id
    @BEGINYEAR INT,                     --year to begin retrieving performance data
    @BEGINMONTH INT,                    --month to begin retrieving performance data
    @ENDYEAR INT,                       --year to end retrieving performance data
    @ENDMONTH INT,                      --month to end retrieving performance data
    @QUARTERLYORMONTHLY VARCHAR(1),     --do you want quarterly or monthly data?
    @VEHICLETYPEID INT,                 --what product vehicle type are you looking for?
    @GROSSORNETID INT                   --are your looking gross of fees data or net of fees data?
)
AS
BEGIN

    SET NOCOUNT ON

    DECLARE @STARTDATE VARCHAR(6),
            @ENDDATE   VARCHAR(6),
            @vBEGINMONTH VARCHAR(2),
            @vENDMONTH VARCHAR(2)   

IF LEN(@BEGINMONTH) = 1 
    SET @vBEGINMONTH = '0' + CAST(@BEGINMONTH AS VARCHAR(1))
ELSE
    SET @vBEGINMONTH = @BEGINMONTH

IF LEN(@ENDMONTH) = 1
    SET @vENDMONTH = '0' + CAST(@ENDMONTH AS VARCHAR(1))
ELSE
    SET @vENDMONTH = @ENDMONTH

SET @STARTDATE = CAST(@BEGINYEAR AS VARCHAR(4)) + @vBEGINMONTH
SET @ENDDATE = CAST(@ENDYEAR AS VARCHAR(4)) + @vENDMONTH

--because null values for gross_or_net_id and vehicle_type_id are represented in 
--multiple ways (true null, empty string, or 0) in the PPQ table, need to account for all possible variations if 
--a -1 is passed in from the .NET code, which represents an enumerated value that
--indicates that the value(s) should be true null.

IF @VEHICLETYPEID = '-1' AND @GROSSORNETID = '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID IS NULL OR PPQ.VEHICLE_TYPE_ID = '0' OR PPQ.VEHICLE_TYPE_ID = '')
        AND (PPQ.GROSS_OR_NET_ID IS NULL OR PPQ.GROSS_OR_NET_ID = '0' OR PPQ.GROSS_OR_NET_ID = '')
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID <> '-1' AND @GROSSORNETID <> '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID = @VEHICLETYPEID )
        AND (PPQ.GROSS_OR_NET_ID = @GROSSORNETID)
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID = '-1' AND @GROSSORNETID <> '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID IS NULL OR PPQ.VEHICLE_TYPE_ID = '0' OR PPQ.VEHICLE_TYPE_ID = '')
        AND (PPQ.GROSS_OR_NET_ID = @GROSSORNETID)
    ORDER BY PPQ.YEARMONTH ASC

IF @VEHICLETYPEID <> '-1' AND @GROSSORNETID = '-1'
    SELECT
        PPQ.YEARMONTH, PPQ.PERFORMANCE
    FROM PRODUCT_PERFORMANCE_QUARTERLY PPQ
        WITH (NOLOCK)
    WHERE 
        (PPQ.PRODUCT_ID = @PRODUCTID)
        AND (PPQ.YEARMONTH BETWEEN @STARTDATE AND @ENDDATE)
        AND (PPQ.QUARTERLY_OR_MONTHLY = @QUARTERLYORMONTHLY)
        AND (PPQ.VEHICLE_TYPE_ID = @VEHICLETYPEID)
        AND (PPQ.GROSS_OR_NET_ID IS NULL OR PPQ.GROSS_OR_NET_ID = '0' OR PPQ.GROSS_OR_NET_ID = '')
    ORDER BY PPQ.YEARMONTH ASC

END

4 个答案:

答案 0 :(得分:1)

我已经看到这种情况发生在过时的索引上。它也可能是一个参数嗅探问题,其中一个不同的查询计划被用于存储过程中的不同参数。

您应该捕获慢速调用的参数,并在每次运行缓慢时查看它们是否相同。

您也可以尝试运行调优向导,看它是否推荐任何索引。

在您可以证明更新和插入发生得太慢(修改索引加上锁定/争用所需的时间),或者您的磁盘空间不足之前,您不必担心索引太多

答案 1 :(得分:0)

听起来像另一个查询正在后台运行已锁定表,而你的无辜查询只是等待它完成

答案 2 :(得分:0)

一个奇怪的边缘案例,但我最近遇到过它。

如果查询在应用程序中运行的时间长于在Management Studio中运行时的查询,则可能需要检查以确保Arithabort已启动。 Management Studio使用的连接参数与.NET使用的连接参数不同。

答案 3 :(得分:0)

这似乎是两件事之一 - 慢速调用的参数在某些方面与快速调用不同,并且它们也无法使用索引,或者存在某种类型的锁定争用这让你感到高兴。你说你在特定进程挂起时检查了阻塞锁,并且没有看到 - 这表明它是第一个。但是 - 您确定您的登台服务器(您无法重现此错误)和开发服务器(您可以重现它)具有相同的数据库配置吗?例如,可能在生产中启用“READ COMMITTED SNAPSHOT”,但在开发中不启用,这会导致读取争用问题在生产中消失。

如果这是参数的差异,我建议使用SQL事件探查器来监视事务并捕获一些 - 一些缓慢的和一些更快的事件,然后,在Management Studio窗口中,替换上面的SP中的变量使用参数值,然后按“Control-L”获取执行计划。这将告诉您SQL Server预期如何处理您的查询,并且您可以比较不同参数组合的执行计划,以查看是否与一个集合存在差异,并从那里开始优化它。

祝你好运!