为何这两个MSSQL索引都有用?是否需替换原有索引?
问题背景
我拥有一个MSSQL数据库表Events,担忧其性能仍有提升空间。
Events表结构及示例数据
| EventId | LocationId | Start | End | Quantity | Price | Currency |
|---|---|---|---|---|---|---|
| 1 | 4 | 2022-08-31 22:00:00.0000000 +02:00 | 2022-08-31 23:00:00.0000000 +02:00 | 7.50000 | 2.0 | EUR |
| 2 | 2 | 2022-04-04 19:00:00.0000000 +01:00 | 2022-04-04 20:00:00.0000000 +01:00 | 1.50000 | 7.5 | EUR |
| 3 | 2 | 2022-04-04 19:00:00.0000000 +01:00 | 2022-04-04 20:00:00.0000000 +01:00 | 4.00000 | 8.2 | EUR |
已创建的非聚集索引
CREATE NONCLUSTERED INDEX [IDX__Events__Location_Start_End] on [Events] ( [LocationId] asc, [Start] asc, [End] asc )
Azure建议创建的索引
CREATE NONCLUSTERED INDEX [IDX__Events__Location_End] ON [dbo].[Events] ([LocationId], [End]) INCLUDE ([Currency], [Price], [Quantity], [Start]) WITH (ONLINE = ON)
补充信息
- 经常执行查询筛选开始时间大于某值且结束时间小于某值的Event。
- 频繁执行以下EF Core代码:
var relevantEvents = await _events.Where($@" [{nameof(Events.LocationId)}] = @locationId and [{nameof(Events.End)}] > @start and [{nameof(Events.Start)}] < @end ", args);
- 同时频繁对该表执行upsert操作。
疑问
这个新增索引为何有用?我是否应该修改原有索引?
解答
新增索引有用的原因
你的核心查询是按LocationId精确匹配,再结合End > @start和Start < @end的范围条件,这个推荐索引针对性极强:
- 索引键顺序匹配查询过滤逻辑:
现有索引键顺序是LocationId → Start → End,但SQL Server使用索引时,精确匹配列放最前之后,仅能对一个列做高效范围过滤(索引有序性决定,范围过滤后后续列的顺序会混乱)。而推荐索引把End作为第二个键,配合LocationId的精确匹配后,能快速筛选出End > @start的记录,剩余的Start < @end过滤可直接在索引包含列上完成,无需额外跳转。 - 覆盖索引消除回表开销:
该索引用INCLUDE包含了查询需要的所有列(Currency、Price、Quantity、Start),属于覆盖索引——查询所需数据全在索引内,不用再访问主表(聚集索引),大幅降低IO成本。你的现有索引未包含这些列,即使能被用到,筛选后仍需回表取数,性能远不如覆盖索引。
是否应该修改原有索引?
需结合业务负载判断:
- 如果没有大量依赖
Start作为主要范围条件的查询(比如Start > @xxx这类高频查询),建议删除原有索引,只保留Azure推荐的索引。索引越多,upsert操作的维护开销越大(每次增改都要更新所有索引),减少冗余索引能直接提升写性能。 - 如果还有以
Start为核心范围过滤的高频查询,则原有索引仍有价值,可保留两个索引,但要评估upsert的性能损失——若upsert频率极高,需权衡读性能提升与写性能损耗,甚至考虑调整查询逻辑来复用索引。
Azure的索引建议基于实际查询负载统计生成,对当前你的高频查询适配性很高,优先启用该索引是合理选择。
内容的提问来源于stack exchange,提问作者wetfield
相关产品推荐
相关产品推荐

