向索引INCLUDE子句添加列对Insert/Update/Delete操作的性能影响
你用到的非聚簇索引创建语句如下:
Create Index IX_01_EmpId On empTable(EmpId) INCLUDE (name, dept, salary, joining_dt)
INCLUDE列对Insert/Update/Delete操作的性能影响
- Insert操作:会产生额外写入开销。每插入一条表数据,除了写入表本身的聚簇索引/堆结构,还需要往IX_01_EmpId索引写入一条包含索引键EmpId和全部INCLUDE列值的条目,相比不带INCLUDE列的同键索引,单条索引条目占用空间更大,写入时消耗的IO资源稍高。
- Update操作:开销和你更新的字段直接相关:
- 如果更新的字段既不是索引键EmpId,也不在INCLUDE列(name、dept、salary、joining_dt)范围内,该索引不需要做任何修改,无额外开销
- 如果更新的是INCLUDE列中的某一个/几个,需要定位到对应索引条目修改对应值,产生少量额外写入开销
- 如果更新的是索引键EmpId,开销最高,需要删除旧索引条目再插入新位置的新条目,甚至可能触发索引页结构调整
- Delete操作:会产生固定的额外删除开销,删除表数据时需要同步删除该索引对应的条目,INCLUDE列的长度对删除开销影响极小,和同键不带INCLUDE的索引的删除开销基本一致。
DML操作对应的索引底层变化
这个非聚簇索引采用B+树结构存储:只有叶子节点存储完整的「索引键+INCLUDE列」组合数据,非叶子节点仅存储索引键EmpId用于层级导航。
- Insert操作底层逻辑:
- 用新记录的EmpId值遍历B+树非叶子节点,定位到需要写入的叶子页
- 把<EmpId, name, dept, salary, joining_dt>组合条目写入叶子页
- 如果叶子页剩余空间不足以容纳新条目,触发页分裂:将原叶子页约一半的数据迁移到新分配的空白叶子页,再写入新条目,同时更新上层非叶子节点的页指向
- Update操作底层逻辑:
- 仅更新INCLUDE列:定位到叶子节点的对应条目后,直接修改对应列的值即可,INCLUDE列不参与索引排序,修改后不会改变条目在索引中的位置,只要修改后的数据未超出叶子页可用空间,不会触发索引结构变动
- 更新索引键EmpId:先删除原有位置的旧索引条目,再将新的<EmpId, 所有INCLUDE列>组合插入到新的对应位置,插入过程可能触发页分裂
- Delete操作底层逻辑:
- 定位到对应叶子页的目标条目,先做逻辑删除标记
- 后续数据库后台清理进程会异步回收被标记删除的条目占用的空间
- 如果单个叶子页内被删除的条目占比达到阈值,会触发相邻叶子页的合并,回收空闲的页空间供后续使用
内容的提问来源于stack exchange,提问作者RobMartin
相关产品推荐
相关产品推荐

