Ste*_*old 8 performance execution-plan sql-server-2016 query-performance
由于特定查询使用了错误的执行计划,我遇到了 100% CPU 峰值的大问题。我现在花了几个星期自己解决。
我的示例数据库包含 3 个简化表。
CREATE TABLE [model].[DataLogger](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ProjectID] [bigint] NULL,
CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE [model].[Inverter](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[SerialNumber] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Inverter] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UK_Inverter] UNIQUE NONCLUSTERED
(
[DataLoggerID] ASC,
[SerialNumber] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [model].[Inverter] WITH CHECK
ADD CONSTRAINT [FK_Inverter_DataLogger]
FOREIGN KEY([DataLoggerID])
REFERENCES [model].[DataLogger] ([ID])
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE [data].[InverterData](
[InverterID] [bigint] NOT NULL,
[Timestamp] [datetime] NOT NULL,
[DayYield] [decimal](18, 2) NULL,
CONSTRAINT [PK_InverterData] PRIMARY KEY CLUSTERED
(
[InverterID] ASC,
[Timestamp] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
Run Code Online (Sandbox Code Playgroud)
该[InverterData]
表包含按月垃圾分区的数百万行(在多个 PaaS 实例中不同)。
所有索引器都进行了碎片整理,并且每天/每周都根据需要重建/重新组织所有统计数据。
查询是实体框架生成的,也很简单。但我每分钟符文 1,000 次,性能至关重要。
SELECT
[Extent1].[InverterID] AS [InverterID],
[Extent1].[DayYield] AS [DayYield]
FROM [data].[InverterDayData] AS [Extent1]
INNER JOIN [model].[Inverter] AS [Extent2] ON [Extent1].[InverterID] = [Extent2].[ID]
INNER JOIN [model].[DataLogger] AS [Extent3] ON [Extent2].[DataLoggerID] = [Extent3].[ID]
WHERE ([Extent3].[ProjectID] = @p__linq__0)
AND ([Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1)
Run Code Online (Sandbox Code Playgroud)
该MAXDOP 1
提示是一个缓慢的计划相同常另一个问题。
在 90% 的时间里,使用的计划是闪电般快速的,看起来像这样:
一天之内,好的计划随机变成了一个糟糕而缓慢的计划。
“坏”计划使用10-60分钟,然后改回“好”计划。“坏”计划使 CPU 永久使用 100%。
这是它的外观:
我的第一个想法是这Hash Match
是坏男孩。所以我用一个新的提示修改了查询。
...Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1, LOOP JOIN)
Run Code Online (Sandbox Code Playgroud)
本LOOP JOIN
应强制使用Nested Loop
的瞬间Hash Match
。
结果是 90% 的计划看起来像以前一样。但是这个计划也随机地变成了一个糟糕的计划。
“坏”计划现在看起来像这样(表循环顺序已更改):
在“新坏”计划期间,CPU 也会达到 100%。
我想到要强制执行“好”计划。但我不知道这是否是个好主意。
计划内是推荐的索引,其中包括所有列。但这将使整个表加倍并减慢高频插入。
请帮我!
这是两个计划(计划中显示了一些额外的字段,因为它来自真实表):
糟糕的计划是随机参数值。所以正常情况下,我@p__linq__1 ='2016-11-26 00:00:00.0000000' @p__linq__0 =20825
的洞天和糟糕的计划都以相同的价值出现。
我知道存储过程中的参数嗅探问题以及如何在 SP 中避免它们。您是否有提示我如何避免我的查询出现此问题?
创建推荐的索引将包括所有列。这将使整个表加倍并减慢插入频率,这是非常频繁的。建立一个简单地克隆表的索引不是“感觉”正确的。另外我的意思是将这个大表的数据大小加倍。
它也不起作用,我认为它不能。为了更好地理解,我将向您解释如何调用查询。
假设我在[DataLogger]
表中有 3 个实体。在一天中,我一遍又一遍地在往返中调用相同的 3 个查询:
基本查询:
...WHERE ([Extent3].[ProjectID] = @p__linq__0) AND ([Extent1].[Date] = @p__linq__1)
范围:
@p__linq__0 = 1; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 2; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 3; @p__linq__1 = '2018-01-05 00:00:00.0000000'
该参数@p__linq__1
始终是相同的日期。但是它会随机选择一个错误的计划,该查询之前使用一个好的计划运行了无数次。同一个参数!
维护每晚运行,看起来像这样。
如果一个索引碎片超过 5%,它将被重新组织......
ALTER INDEX [{index}] ON [{table}] REORGANIZE
如果索引碎片超过 30%,它将被重建......
ALTER INDEX [{index}] ON [{table}] REBUILD WITH (ONLINE=ON, MAXDOP=1)
如果索引已分区,它将证明碎片并更改每个分区...
ALTER INDEX [{index}] ON [{table}] REBUILD PARTITION = {partitionNr} WITH (ONLINE=ON, MAXDOP=1)
如果modification_counter
高于 0,所有统计数据都将更新...
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH FULLSCAN
或在分区..
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH RESAMPLE ON PARTITIONS({partitionNr})
维护包括所有统计数据,也包括自动生成的统计数据。
小智 3
看看计划,好的和坏的还是有一些区别的。首先要注意的是,好的计划在 InverterDayData 上执行查找,而坏的计划则执行扫描。这是为什么,如果您检查估计的行数,您会发现好的计划预计 1 行,而坏计划预计 6661 行和大约 7000 行。
现在看一下编译后的参数值,
好计划 @p__linq__1 ='2016-11-26 00:00:00.0000000'@p__linq__0 =20825
糟糕的计划 @p__linq__1 ='2018-01-03 00:00:00.0000000' @p__linq__0 = 20686
所以在我看来,这是一个参数嗅探问题,当查询性能不佳时,您将哪些参数值传递到该查询中?
InverterDayData 上的坏计划中有一个索引建议看起来很合理,我会尝试运行它并看看它是否对您有帮助。它可能允许 SQL 对表执行扫描。
归档时间: |
|
查看次数: |
1284 次 |
最近记录: |