Jer*_*yow 13 sql sql-server performance
背景
我们最近遇到了sql server在我们的一个较大的表(大约175,000,000行)上使用的查询计划的问题.该表的列和索引结构未发生5年以上的变化.
表和索引如下所示:
create table responses (
response_uuid uniqueidentifier not null,
session_uuid uniqueidentifier not null,
create_datetime datetime not null,
create_user_uuid uniqueidentifier not null,
update_datetime datetime not null,
update_user_uuid uniqueidentifier not null,
question_id int not null,
response_data varchar(4096) null,
question_type_id varchar(3) not null,
question_length tinyint null,
constraint pk_responses primary key clustered (response_uuid),
constraint idx_responses__session_uuid__question_id unique nonclustered (session_uuid asc, question_id asc) with (fillfactor=80),
constraint fk_responses_sessions__session_uuid foreign key(session_uuid) references dbo.sessions (session_uuid),
constraint fk_responses_users__create_user_uuid foreign key(create_user_uuid) references dbo.users (user_uuid),
constraint fk_responses_users__update_user_uuid foreign key(update_user_uuid) references dbo.users (user_uuid)
)
create nonclustered index idx_responses__session_uuid_fk on responses(session_uuid) with (fillfactor=80)
Run Code Online (Sandbox Code Playgroud)
表现不佳的查询(约2.5分钟而不是正常的<1秒表现)看起来像这样:
SELECT
[Extent1].[response_uuid] AS [response_uuid],
[Extent1].[session_uuid] AS [session_uuid],
[Extent1].[create_datetime] AS [create_datetime],
[Extent1].[create_user_uuid] AS [create_user_uuid],
[Extent1].[update_datetime] AS [update_datetime],
[Extent1].[update_user_uuid] AS [update_user_uuid],
[Extent1].[question_id] AS [question_id],
[Extent1].[response_data] AS [response_data],
[Extent1].[question_type_id] AS [question_type_id],
[Extent1].[question_length] AS [question_length]
FROM [dbo].[responses] AS [Extent1]
WHERE [Extent1].[session_uuid] = @f6_p__linq__0;
Run Code Online (Sandbox Code Playgroud)
(查询由实体框架生成并使用sp_executesql执行)
表现不佳期间的执行计划如下所示:

关于运行上述查询的数据的一些背景将永远不会返回超过400行.换句话说,对session_uuid进行过滤实际上削减了结果集.
有关计划维护的一些背景知识 - 计划作业每周运行一次,以重建数据库的统计信息并重建表的索引.该作业运行如下所示的脚本:
alter index all on responses rebuild with (fillfactor=80)
Run Code Online (Sandbox Code Playgroud)
性能问题的解决方案是在此表上运行重建索引脚本(上面).
其他可能相关的信息花絮......自上次索引重建以来,数据分发根本没有变化.查询中没有联接.我们是一个SAAS商店,我们拥有50到100个具有完全相同模式的实时生产数据库,一些数据更多,一些数据更少,所有执行相同的查询都分布在几个sql服务器上.
题:
可能会发生什么会让sql server在这个特定的数据库中开始使用这个可怕的执行计划?
请记住,只需重建表上的索引即可解决问题.
也许一个更好的问题是"sql server停止使用索引的情况是什么?"
查看它的另一种方法是"为什么优化器不会使用几天前重建的索引,然后在我们注意到错误的查询计划后执行索引的紧急重建后再次开始使用它?"
这个评论太长了.
原因很简单:优化器改变了对最佳计划的看法.这可能是由于数据分布的细微变化(或其他原因,例如join密钥中的类型不兼容).我希望有一个工具不仅可以为查询提供执行计划,还可以显示与另一个执行计划的接近程度的阈值.或者一种工具,可以让您存储执行计划,并在相同的查询开始使用不同的计划时发出警报.
我不止一次地问自己这个完全相同的问题.你有一个系统每晚运行几个月.它使用非常复杂的查询处理大量数据.然后,有一天,你早上进来,通常在晚上11点结束的工作仍在运行.Arrrggg!
我们提出的解决方案是join对失败的连接使用显式提示.(option (merge join, hash join)).我们还开始为所有复杂查询保存执行计划,因此我们可以比较从一个晚上到下一个的变化.最后,这比实际利益更具学术兴趣 - 当计划改变时,我们已经遭受了糟糕的执行计划.