从.NET应用程序到SQL Server的缓慢调用
我在SQL Server中有一个表,该表是从仍在生产中的旧系统继承的,该旧系统是根据以下代码构造的。我创建了一个SP来查询表,如表create语句下面的代码中所述。我的问题是,偶尔通过企业库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
解决方案
回答
听起来好像另一个查询正在后台运行,该查询已锁定了表,而我们无辜的查询只是在等待它完成
回答
我已经看到过时的索引会发生这种情况。这也可能是参数嗅探问题,其中对存储过程中使用的不同参数使用了不同的查询计划。
我们应该捕获慢速调用的参数,并在每次慢速运行时查看它们是否相同。
我们也可以尝试运行调整向导,看看它是否建议任何索引。
我们不必担心索引过多,直到我们可以证明更新和插入的速度太慢(修改索引加上锁定/争用所需的时间),或者它们的磁盘空间用完了。
回答
一个奇怪的边缘案例,但我最近遇到了它。
如果查询在应用程序中的运行时间长于在Management Studio中运行的查询,则可能需要检查以确保Arithabort被关闭。 Management Studio使用的连接参数不同于.NET使用的连接参数。
回答
看来这是两件事之一,即慢速调用上的参数与快速调用上的参数有所不同,并且它们也无法使用索引,或者某种类型的锁定争用阻止了我们。我们说我们已在挂起特定进程时检查了锁定锁,却没有发现任何暗示它是第一个的锁。但是,我们确定登台服务器(不能重现此错误)和开发服务器(可以重现此错误)具有相同的数据库配置吗?例如,可能在生产中启用了" READ COMMITTED SNAPSHOT",但在开发中未启用,这将导致读取争用问题在生产中消失。
如果参数有所不同,建议我们使用SQL Profiler监视事务并捕获一些慢速和快速的事务,然后在Management Studio窗口中,用参数值替换该SP中的变量。然后按" Control-L"获得执行计划。这将准确告诉我们SQL Server期望如何处理查询,并且我们可以比较不同参数组合的执行计划,以查看一组参数是否存在差异,然后从那里进行优化。
祝你好运!