背景
最近,我们在一个较大的表(大约 175,000,000 行)上使用 sql server 的查询计划时遇到了问题。该表的列和索引结构已经 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)
性能不佳的查询(大约 2.5 分钟而不是正常的
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;
(查询由实体框架生成并使用sp_executesql执行)
性能不佳期间的执行计划如下所示:
有关数据的一些背景 - 运行上面的查询永远不会返回超过 400 行。换句话说,对 session_uuid 进行过滤确实减少了结果集。
有关计划维护的一些背景知识 - 计划作业每周运行一次以重建数据库的统计信息并重建表的索引。该作业运行一个如下所示的脚本:
alter index all on responses rebuild with (fillfactor=80)
性能问题的解决方案是在此表上运行重建索引脚本(见上文)。
其他可能相关的信息花絮...自上次索引重建以来,数据分布根本没有改变。查询中没有连接。我们是一家 SAAS 商店,我们有 50 - 100 个实时生产数据库,它们具有完全相同的架构,有些数据较多,有些数据较少,所有数据库都在几个 sql 服务器上执行相同的查询。
问题:
可能会发生什么情况导致 sql server 开始在这个特定的数据库中使用这个糟糕的执行计划?
请记住,只需重建表上的索引即可解决问题。
也许更好的问题是“sql server 在什么情况下会停止使用索引?”
另一种看待它的方式是“为什么优化器不使用几天前重建的索引,然后在我们注意到错误的查询计划后紧急重建索引后再次开始使用它?”