如何识别SQL Server中需要添加索引的查询
引言
在数据库性能优化中,索引是提升查询速度最有效的手段之一。然而,不恰当的索引会降低写操作性能并增加存储开销。作为DBA,我们经常面临这样的挑战:如何精准定位哪些查询真正需要添加索引? 本文将分享几种实用的T-SQL查询,帮助您科学识别缺失索引,并提供最佳实践指南。
一、为什么需要索引优化?
性能瓶颈:全表扫描(Table Scan)可能导致简单查询耗时数秒
资源浪费:未使用索引的查询消耗额外CPU和I/O资源
隐性成本:缺失索引可能使关键业务操作延迟数倍
据统计,合理添加索引可使查询性能提升10-100倍(来源:Microsoft SQL Server性能调优白皮书)
二、核心诊断查询
1. 缺失索引自动生成脚本
SELECT TOP 10
ROUND(migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans), 0) AS improvement_measure,
DB_NAME(mid.database_id) AS database_name,
OBJECT_NAME(mid.object_id) AS table_name,
'CREATE INDEX [IX_' + OBJECT_NAME(mid.object_id) + '_'
+ REPLACE(REPLACE(REPLACE(ISNULL(mid.equality_columns, ''), ', ', '_'), '[', ''), ']', '')
+ CASE WHEN mid.inequality_columns IS NOT NULL THEN '_' + REPLACE(REPLACE(REPLACE(mid.inequality_columns, ', ', '_'), '[', ''), ']', '') ELSE '' END
+ '] ON ' + mid.statement
+ ' (' + ISNULL(mid.equality_columns, '')
+ CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END
+ ISNULL(mid.inequality_columns, '') + ')'
+ ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement,
migs.user_seeks AS seek_operations,
migs.avg_user_impact AS improvement_percent
FROM sys.dm_db_missing_index_group_stats AS migs
INNER JOIN sys.dm_db_missing_index_groups AS mig
ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS mid
ON mig.index_handle = mid.index_handle
WHERE mid.database_id = DB_ID()
ORDER BY improvement_measure DESC;
结果解读:
improvement_measure:综合改进指标(值越大优先级越高)improvement_percent:预估查询性能提升百分比seek_operations:该索引可能被使用的次数
2. 高开销扫描查询定位
SELECT TOP 5
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
qs.execution_count,
SUBSTRING(st.text, (qs.statement_start_offset/2) + 1,
((CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(st.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset)/2) + 1) AS query_text,
qp.query_plan
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE qp.query_plan.exist('//RelOp[@PhysicalOp="Index Scan" or @PhysicalOp="Clustered Index Scan"]') = 1
ORDER BY avg_logical_reads DESC;
关键指标:
avg_logical_reads> 1000 表示严重I/O问题执行计划中出现
Index Scan警告
3. 未索引的热点列检测
SELECT TOP 10 t.name AS TableName, c.name AS ColumnName, SUM(us.user_scans) AS total_scans FROM sys.tables t JOIN sys.columns c ON t.object_id = c.object_id LEFT JOIN sys.index_columns ic ON ic.object_id = t.object_id AND ic.column_id = c.column_id LEFT JOIN sys.indexes i ON i.object_id = t.object_id AND i.index_id = ic.index_id LEFT JOIN sys.dm_db_index_usage_stats us ON us.object_id = t.object_id AND us.index_id = i.index_id WHERE i.index_id IS NULL -- 无索引列 AND us.user_scans > 0 GROUP BY t.name, c.name ORDER BY total_scans DESC;
三、索引创建黄金法则
1. 索引设计原则
-- 标准结构 CREATE INDEX IX_Table_KeyColumns ON dbo.Table (Column1 ASC, Column2 DESC) INCLUDE (Column3, Column4) WITH (FILLFACTOR = 90); -- 针对频繁更新表 -- 筛选索引(针对热点数据) CREATE INDEX IX_Orders_Active ON dbo.Orders (OrderDate) WHERE Status = 'Processing';
2. 四要四不要
| 该做的 | 避免的 |
|---------------------------|--------------------------|
| 优先选择高选择性列 | 在bit类型列建索引 |
| INCLUDED列放常用查询字段 | 创建重复功能索引 |
| 定期重建碎片率>30%的索引 | 盲目接受所有系统建议 |
| 测试环境验证性能提升 | 在生产环境直接创建索引 |
四、高级技巧
1. 索引使用监控
SELECT OBJECT_NAME(ix.object_id) AS TableName, ix.name AS IndexName, ix.type_desc AS IndexType, us.user_seeks, us.user_scans, us.user_lookups, us.user_updates FROM sys.dm_db_index_usage_stats us JOIN sys.indexes ix ON us.object_id = ix.object_id AND us.index_id = ix.index_id WHERE us.database_id = DB_ID() AND OBJECTPROPERTY(us.object_id, 'IsUserTable') = 1;
决策依据:
user_updates> 10 * (user_seeks+user_scans) → 考虑删除索引user_lookups过高 → 需要优化INCLUDED列
2. 查询存储深度分析(SQL Server 2016+)
SELECT q.query_id, t.query_sql_text, rs.avg_duration, rs.avg_logical_io_reads, p.query_plan FROM sys.query_store_query q JOIN sys.query_store_query_text t ON q.query_text_id = t.query_text_id JOIN sys.query_store_plan p ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id WHERE rs.last_execution_time > DATEADD(DAY, -7, GETDATE()) ORDER BY rs.avg_logical_io_reads DESC;
五、避坑指南
索引覆盖陷阱:包含过多INCLUDED列会显著增大索引体积
参数嗅探问题:使用
OPTION(RECOMPILE)解决参数敏感查询锁升级风险:单索引超过8KB可能引发锁升级
统计信息滞后:开启
AUTO_UPDATE_STATISTICS_ASYNC
结语
精准的索引优化需要持续监控和迭代调整。建议每周运行一次诊断查询,重点关注:
改进潜力(improvement_measure) > 100,000 的索引
逻辑读取(avg_logical_reads) > 5000 的查询
扫描次数(total_scans) > 10,000 的热点列
附录工具推荐:
sp_BlitzIndex - 索引分析神器
Database Engine Tuning Advisor - 微软官方调优工具
SolarWinds DPA - 商业级性能监控平台
通过科学诊断和谨慎实施,您可以将查询性能提升300%以上!欢迎在评论区分享您的索引优化实战经验。
如何识别SQL Server中需要添加索引的查询的更多相关文章
- 如何识别SQL Server中的IO瓶颈
原文:如何识别SQL Server中的IO瓶颈 原文出自: http://www.mssqltips.com/sqlservertip/2329/how-to-identify-io-bottlene ...
- 如何识别SQL Server中的CPU瓶颈
原文:如何识别SQL Server中的CPU瓶颈 原文出自: http://www.mssqltips.com/sqlservertip/2316/how-to-identify-sql-server ...
- SQL Server中INNER JOIN与子查询IN的性能测试
这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...
- SQL Server中Table字典数据的查询SQL示例代码
SQL Server中Table字典数据的查询SQL示例代码 前言 在数据库系统原理与设计(第3版)教科书中这样写道: 数据库包含4类数据: 1.用户数据 2.元数据 3.索引 4.应用元数据 其中, ...
- SQL Server中的聚集索引(clustered index) 和 非聚集索引 (non-clustered index)
本文转载自 http://blog.csdn.net/ak913/article/details/8026743 面试时经常问到的问题: 1. 什么是聚合索引(clustered index) / ...
- sql server中使用组合索引需要注意的地方
一.使用组合索引需要注意的地方 1.索引应该建在选择性高的字段上(键值唯一的记录数/总记录条数),选择性越高索引的效果越好.价值越大,唯一索引的选择性最高: 2.组合索引中字段的顺序,选择性越高的字段 ...
- 【译】索引进阶(七):SQL SERVER中的过滤索引
原文链接:传送门. To be continued...
- SQL SERVER中生僻字问题存储与查询问题
以下仅记录碰到的几个问题 1.首先字段设置为varchar的时候存储后无法进行正常的显示 显示为? 此状态下匹配查询或者Like模糊查询都没问题 2.将字段设置为nvarchar,在进行插入或者跟新时 ...
- SQL Server中一些不常见的查询
把一些不常见但又会用到的SQL查询整理备份一下 --筛选出某个字段中包含中文的记录 SELECT * FROM temp WHERE W1 LIKE '%[吖-座]%' --筛选出某个字段在哪些表中存 ...
- sql server中数据约束相关的查询
根据表名查找数据约束 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'CMS_EventLog'; SEL ...
随机推荐
- 第10章面向对象编程(高级部分)-cnblog
类变量与类方法 static修饰的成员变量(类变量,静态变量)的特性? 同一个类所有对象共享 类变量是随着类的加载而创建, 所以即使没有创建对象实例也可以访问 ,但是类变量的访问, 必须遵守 相关的访 ...
- C#/.NET/.NET Core技术前沿周刊 | 第 32 期(2025年3.24-3.31)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- Spring的三级缓存详解
目录 1.什么是三级缓存 2.三级缓存详解 Bean实例化前 属性赋值/注入前 初始化后 总结 3.怎么解决的循环依赖 4.不用三级缓存不行吗 5.总结 一.什么是三级缓存 就是在Bean生成流程中保 ...
- jmeter使用正则表达式提取器提取返回值中的数据
场景描述:测试过程中,一个场景经常会调用几个接口,且前后接口之间存在参数传递.前一个接口返回值提取后传给后一个接口使用 操作步骤: 第一步,选中被提取参数的接口请求,依次点击右键-添加-后置处理器-正 ...
- 加减法计算在RB中的应用(比如计算库存)(should be equal as integers指令的使用)
订单测试过程中,对库存的校验是很关键的步骤 下面这个案例即实现对订单前后库存检查.公式计算.结果匹配,输出测试结果.具体脚本如下图 步骤如下: 1.获取订单前的库存 2.订单流程 3.获取订单后的库存 ...
- eolinker环境变量配置:用例执行前给把某参数设置为全局参数的方法
特别注意:需要使用全局变量或者预处理前务必阅读本链接https://www.cnblogs.com/becks/p/13713278.html 1.场景分析 注册会员流程共计有添加数据,校验数据,提交 ...
- WinDebug查看C#程序运行内存中的数据库连接字符串
真巧,昨天刷到了大佬"一线码农"的视频,大概就是讲的有人找他破解一个混淆加密的数据库连接字符串,然后大佬也提供了方案就是用WinDebug查看内存中的数据.这其实本质上就是一个用W ...
- 为什么 G1 垃圾收集器不维护年轻代到老年代的记忆集?
为什么 G1 垃圾收集器不维护年轻代到老年代的记忆集? 在 G1 垃圾收集器中,不维护年轻代到老年代的记忆集(Remembered Set, RSet)是因为其设计特点和优化策略使得这种记忆集的维护既 ...
- 17.6K star!后端接口零代码的神器来了,腾讯开源的ORM库太强了!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 " 实时零代码.全功能.强安全 ORM 库 后端接口和文档零代码,前端定制返回 J ...
- Spring Boot Jpa封装快速构建Specification、OrderBy、Pageable的查询条件
1.简介 在我们使用JPA时,构建 Specification 查询条件时重复代码过多,而且需要大量的无效代码. 2.工具类提供的方法 2.1.自动构建规范 /** * 自动构建规范 * * @p ...