SQL Server 2022亿级表:能否合并两个非聚集索引?
能否合并两个非聚集索引?
结论:可以合并,但需要结合实际查询场景评估优缺点,以下是具体分析:
原索引的作用场景
原索引1
create nonclustered index [IX_SMSInOutBoxDetails_SMSInOutBox_ID_SendStatus] on [dbo].[SMSInOutBoxDetails] ( [SMSInOutBox_ID] asc, [SendStatus] asc ) include ([Cost]) on [PRIMARY]
覆盖以SMSInOutBox_ID和SendStatus为筛选/排序条件,仅需获取Cost字段的查询。
原索引2
create nonclustered index [IX_SMSInOutBoxDetails_SMSInOutBox_ID_SendTime] on [dbo].[SMSInOutBoxDetails] ( [SMSInOutBox_ID] asc, [SendTime] asc ) include ([ID], [Number], [Delivery], [Cost], [RefId], [SendStatus], [IsRead], [MobileOperator], [WebServiceOutboxDetailId], [Order], [sort]) on [PRIMARY]
覆盖以SMSInOutBox_ID和SendTime为筛选/排序条件,需要获取包含列中所有字段的查询。
拟合并索引的覆盖能力
create nonclustered index [IX_SMSInOutBoxDetails_SMSInOutBox_ID_SendStatus_SendTime] on [dbo].[SMSInOutBoxDetails] ( [SMSInOutBox_ID] asc, [SendStatus] asc, [SendTime] asc ) include ([ID], [Number], [Delivery], [Cost], [RefId], [IsRead], [MobileOperator], [WebServiceOutboxDetailId], [Order], [sort]) on [PRIMARY]
- 完全覆盖原索引1的查询需求:键列包含
SMSInOutBox_ID和SendStatus,包含列也有Cost,原索引1的查询可以直接使用该合并索引。 - 部分覆盖原索引2的查询需求:若查询同时包含
SMSInOutBox_ID、SendStatus和SendTime的筛选/排序条件,该索引效率和原索引2相当;但如果查询仅以SMSInOutBox_ID和SendTime为条件(无SendStatus),则该索引的查询效率会低于原索引2——因为键列顺序是先SendStatus后SendTime,SQL Server需要在相同SMSInOutBox_ID下扫描所有SendStatus值来定位SendTime,而原索引2是直接按SendTime排序,定位更快。
合并的优缺点
优点
- 降低DML维护开销:1亿条记录的大表,每次插入、更新、删除操作时,原两个索引都需维护,合并后仅需维护一个,能显著减少DML操作的性能损耗。
- 节省存储空间:合并后的索引仅存储一份键列和包含列,比两个原索引的总存储空间小,对于大表来说节省的空间非常可观。
潜在风险
- 特定查询性能下降:如果系统中有大量仅基于
SMSInOutBox_ID和SendTime的高频查询,合并后的索引会导致这类查询的IO次数增加、响应变慢。 - 索引页利用率降低:合并后的索引键列更长,单页能存储的索引条目数减少,会增加查询时的IO次数。
验证建议
- 梳理所有依赖原索引的查询:重点确认原索引2对应的查询是否大多包含
SendStatus条件,若此类查询占比高,合并的收益更大。 - 做性能对比测试:先创建合并后的索引,禁用原两个索引,运行关键查询,通过
SET STATISTICS IO, TIME ON对比执行时间、逻辑读等指标。 - 监控索引使用情况:通过
sys.dm_db_index_usage_stats查看原索引的使用频率,如果原索引1使用极少,合并的优先级更高;若原索引2的高频查询不受合并影响,则可以放心合并。
内容的提问来源于stack exchange,提问作者SnowStorm
相关产品推荐
相关产品推荐

