引言

在数据库性能优化中,索引是提升查询速度最有效的手段之一。然而,不恰当的索引会降低写操作性能并增加存储开销。作为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;

  

五、避坑指南

  1. 索引覆盖陷阱:包含过多INCLUDED列会显著增大索引体积

  2. 参数嗅探问题:使用OPTION(RECOMPILE)解决参数敏感查询

  3. 锁升级风险:单索引超过8KB可能引发锁升级

  4. 统计信息滞后:开启AUTO_UPDATE_STATISTICS_ASYNC

结语

精准的索引优化需要持续监控和迭代调整。建议每周运行一次诊断查询,重点关注:

  • 改进潜力(improvement_measure) > 100,000 的索引

  • 逻辑读取(avg_logical_reads) > 5000 的查询

  • 扫描次数(total_scans) > 10,000 的热点列


附录工具推荐:

  1. sp_BlitzIndex - 索引分析神器

  2. Database Engine Tuning Advisor - 微软官方调优工具

  3. SolarWinds DPA - 商业级性能监控平台

通过科学诊断和谨慎实施,您可以将查询性能提升300%以上!欢迎在评论区分享您的索引优化实战经验。

如何识别SQL Server中需要添加索引的查询的更多相关文章

  1. 如何识别SQL Server中的IO瓶颈

    原文:如何识别SQL Server中的IO瓶颈 原文出自: http://www.mssqltips.com/sqlservertip/2329/how-to-identify-io-bottlene ...

  2. 如何识别SQL Server中的CPU瓶颈

    原文:如何识别SQL Server中的CPU瓶颈 原文出自: http://www.mssqltips.com/sqlservertip/2316/how-to-identify-sql-server ...

  3. SQL Server中INNER JOIN与子查询IN的性能测试

    这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...

  4. SQL Server中Table字典数据的查询SQL示例代码

    SQL Server中Table字典数据的查询SQL示例代码 前言 在数据库系统原理与设计(第3版)教科书中这样写道: 数据库包含4类数据: 1.用户数据 2.元数据 3.索引 4.应用元数据 其中, ...

  5. SQL Server中的聚集索引(clustered index) 和 非聚集索引 (non-clustered index)

    本文转载自  http://blog.csdn.net/ak913/article/details/8026743 面试时经常问到的问题: 1. 什么是聚合索引(clustered index) / ...

  6. sql server中使用组合索引需要注意的地方

    一.使用组合索引需要注意的地方 1.索引应该建在选择性高的字段上(键值唯一的记录数/总记录条数),选择性越高索引的效果越好.价值越大,唯一索引的选择性最高: 2.组合索引中字段的顺序,选择性越高的字段 ...

  7. 【译】索引进阶(七):SQL SERVER中的过滤索引

    原文链接:传送门. To be continued...

  8. SQL SERVER中生僻字问题存储与查询问题

    以下仅记录碰到的几个问题 1.首先字段设置为varchar的时候存储后无法进行正常的显示 显示为? 此状态下匹配查询或者Like模糊查询都没问题 2.将字段设置为nvarchar,在进行插入或者跟新时 ...

  9. SQL Server中一些不常见的查询

    把一些不常见但又会用到的SQL查询整理备份一下 --筛选出某个字段中包含中文的记录 SELECT * FROM temp WHERE W1 LIKE '%[吖-座]%' --筛选出某个字段在哪些表中存 ...

  10. sql server中数据约束相关的查询

    根据表名查找数据约束 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'CMS_EventLog'; SEL ...

随机推荐

  1. Python科学计算系列12—积分变换

    1.拉普拉斯变换及逆变换 拉普拉斯变换公式 拉普拉斯逆变换公式 例子: 代码如下: from sympy import * from sympy.integrals import laplace_tr ...

  2. 使用 StreamJsonRpc 在 ASP.NET Core 中启用 JSON-RPC

    StreamJsonRpc 是微软开发的一个开源库,用于在 .NET 平台中实现基于 JSON-RPC 2.0 规范 的远程过程调用(RPC).它通过流(如管道.网络流等)实现高效的跨进程或跨网络通信 ...

  3. 【一步步开发AI运动APP】八、自定义姿态动作识别检测——之姿态相似度比较

    之前我们为您分享了[一步步开发AI运动小程序]开发系列博文,通过该系列博文,很多开发者开发出了很多精美的AI健身.线上运动赛事.AI学生体测.美体.康复锻炼等应用场景的AI运动小程序:为了帮助开发者继 ...

  4. office for mac 16.79 破解版安装教程

    教程声明 本人电脑系统:macOS Sonoma,安装版本为office for mac 16.79.本教程旨在学习分享.资源均为从网络处下载,安装破解版有风险,请自己权衡.不会安装的朋友可评论区探讨 ...

  5. 使用PowerShell开发脚本程序进行批量SVN提交

    使用PowerShell开发脚本程序进行批量SVN提交 随着软件开发的不断进步,版本控制系统如Subversion (SVN) 成为了团队协作和代码管理的重要工具.当需要一次性提交大量文件时,手动操作 ...

  6. 11.7K Star!这个分布式爬虫管理平台让多语言协作如此简单!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 分布式爬虫管理平台Crawlab,支持任何编程语言和框架的爬虫管理,提供可视化界面.任务调度 ...

  7. 用AI做了个动态下发微信群二维码应用

    微信群的二维码每周都要更新一次,比较麻烦.于是搞了个简单的上传/下发的 Web 应用. 下面是优化前后流程,虽然看似步骤少了一步,但大大节省了时间. 主要功能 常见类型图片上传,支持删除,提供外链访问 ...

  8. Everyone's Favorite Linear, Direct Access, Homogeneous Data Structure: The Array(英翻中)

    Arrays are one of the simplest and most widely used data structures in computer programs. Arrays in ...

  9. .NET 的全新低延时高吞吐自适应 GC - Satori GC

    GC 的 STW 问题 GC,垃圾回收器,本质上是一种能够自动管理自己分配的内存的生命周期的内存分配器.这种方法被大多数流行编程语言采用,然而当你使用垃圾回收器时,你会失去对应用程序如何管理内存的控制 ...

  10. 第1.1讲:Transformers 的崛起:从RNN到Self-Attention

    序列建模的演进之路 一.RNN( Recurrent Neural Networks):序列处理的开拓者 循环神经网络(RNN)是最早处理序列数据的深度学习结构.RNN的核心思想是在处理序列的每个时间 ...