SQL Server 性能调优 之运行计划(Execution Plan)调优
运行计划中的三种 Join 策略
SQL Server 存在三种 Join 策略:Hash Join,Merge Join,Nested Loop Join。
Hash Join:用来处理没有排过序/没有索引的数据,它在内存中把 Join 两边数据(的关联key)分别建立一个哈希表。比如有下面的查询语句,关联的两张表没有建立索引,运行计划将显示为Hash Join。
SELECT
sh.*
FROM
SalesOrdHeaderDemo AS sh
JOIN
SalesOrdDetailDemo AS sd
ON
sh.SalesOrderID=sd.SalesOrderID
GO
Merge Join:用来处理有索引的数据,它比Hash Join轻量化。我们为前面两张表的关联列建立索引,然后再次上面的查询,运行计划将变更为Merge Join
CREATE UNIQUE CLUSTERED INDEX idx_salesorderheaderdemo_SalesOrderID ON SalesOrdHeaderDemo (SalesOrderID)
GO
CREATE UNIQUE CLUSTERED INDEX idx_SalesDetail_SalesOrderlID ON SalesOrdDetailDemo (SalesOrderID,SalesOrderDetailID)
GO
Nested Loop Join:在满足Merge Join的基础上,假设某一边的数据较少,那么SQL Server 会把数据较少的那个作为外部循环,还有一个作为内部循环来完毕Join处理。继续前面的样例为查询语句加上WHERE语句来降低 Join 一边的数据量,运行计划显示为Nested Loop Join。
SELECT
sh.*
FROM
SalesOrdHeaderDemo AS sh
JOIN
SalesOrdDetailDemo AS sd
ON
sh.SalesOrderID=sd.SalesOrderID
WHERE
sh.SalesOrderID=43659
运行计划中的(table/index scan)的改进
在很多场合我们须要在一张包括很多数据的表中提取出一小部分数据,此时应当避免Scan,由于扫描处理会遍历每一行,这是相当耗时耗力的。以下我们来看一个样例:
SELECT
sh.SalesOrderID
FROM
SalesOrdHeaderDemo AS sh
JOIN
SalesOrdDetailDemo AS sd
ON
sh.SalesOrderID=sd.SalesOrderID
WHERE
sh.OrderDate='2005-07-01 00:00:00.000'
GO
图中的红圈标出了table scan,而且运行计划也智能得建议建立索引。我们先尝试在SalesOrdHeader 表上建立一个索引:
CREATE UNIQUE CLUSTERED INDEX idx_salesorderheaderdemo_SalesOrderID ON SalesOrdHeaderDemo (SalesOrderID)
GO
然后再次运行同样的查询语句,运行计划变成下面的模样:
table scan 变为了 Index Scan,继续给还有一张表也加上索引:
CREATE UNIQUE CLUSTERED INDEX idx_SalesDetail_SalesOrderlID ON SalesOrdDetailDemo (SalesOrderID,SalesOrderDetailID)
GO
运行计划发生下面的变化:
尽管不能说 Scan 比 Seek 差,但绝大多数的场合(尤其是在很多数据中查找少量数据时)Seek 是更好的选择。举例来说假设你有一个上亿条数据的表,你要取当中的100条,那么你应当保证其採用 Seek,但假设你须要取出当中绝大多数(比方95%)的数据时,Scan 可能更好。(有较权威的文章给出了这个阀值为30%,即取出超过30%数据时 scan 更高效;反之则 Seek 更好)
另外你可能注意到两张表上都建立了索引但一张表在运行计划中表现为 Clustered index scan,而还有一张表现为 Clustered index seek,我们期待的不是两个 Clustered index seek 吗?这是由于前一张表没有断言(predicate),而后一张表通过 ON keyword对SalesOrderID 进行了断言限制。
运行计划中的 Key Lookup
为了兴许的演示样例,我们先在同一张表上建立两个不同的索引:
CREATE UNIQUE CLUSTERED INDEX idx_SalesDetail_SalesOrderlID ON SalesOrdDetailDemo (SalesOrderID,SalesOrderDetailID)
GO
CREATE NONCLUSTERED INDEX idx_non_clust_SalesOrdDetailDemo_ModifiedDate ON SalesOrdDetailDemo(ModifiedDate)
GO
运行下面的查询:
SELECT
ModifiedDate
FROM SalesOrdDetailDemo
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
运行计划例如以下图,他利用了我们先前建立在 ModifiedDate 字段上的 Non-Clustered Index,生成为一个Index Seek 处理。
我们改造一下查询语句,SELECT 中多加两个字段:
SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID
FROM SalesOrdDetailDemo
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
运行计划例如以下图,基本没变:
上面选出的字段不是属于 Non-Clustered Index 就是属于 Clustered Index,假设再添加几个其它的字段呢?
SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
乖乖,运行计划一下多了两个处理(Key Lookup, Nested Loop):
Key Lookup 是一个繁重的处理,我们能够使用keyword WITH 来指定使用 Clustered Index,以此回避Key Lookup。
SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo WITH(INDEX=idx_SalesDetail_SalesOrderlID)
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
运行计划应声而变成为一个 Clustered Index Scan:
前文提过 Scan 似乎也不是一个非常好的处理,那么矮子里拔高个,使用 SET STATISTICS IO ON 来比較一下:
SET STATISTICS IO ON
GO SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo WITH(INDEX=idx_SalesDetail_SalesOrderlID)
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo WITH(INDEX=idx_non_clust_SalesOrdDetailDemo_ModifiedDate)
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
比較下来,採用了 clustered index 的查询表现最差,另外 SET STATISTICS IO 输出的数据中clustered index 的查询在 logical reads 上花费了很多其它的时间。
看起来採用 non-clustered index + Key Lookup 运行计划表现还不错,但假设能回避 Key Lookup 就完美了,我们来把 non-clustered index 改动一下,用 INCLUDE keyword在索引中包括其它的字段:
DROP INDEX idx_non_clust_SalesOrdDetailDemo_ModifiedDate ON SalesOrdDetailDemo
GO
CREATE NONCLUSTERED INDEX idx_non_clust_SalesOrdDetailDemo_ModifiedDate ON SalesOrdDetailDemo(ModifiedDate)
INCLUDE
(
ProductID,
UnitPrice
)
GO -- 清下缓存,仅用于开发环境!
DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS
GO
再次运行之前的查询:
SELECT
ModifiedDate,
SalesOrderID,
SalesOrderDetailID,
ProductID,
UnitPrice
FROM SalesOrdDetailDemo
WHERE ModifiedDate='2005-07-01 00:00:00.000'
GO
这下完美了,由于我们的查询字段都包括在索引中,所以运行计划终于被优化为 Index Seek。
SQL Server 性能调优 之运行计划(Execution Plan)调优的更多相关文章
- (转)SQL Server 性能调优(cpu)
摘自:http://www.cnblogs.com/Amaranthus/archive/2012/03/07/2383551.html 研究cpu压力工具 perfom SQL跟踪 性能视图 cpu ...
- SQL Server 性能调优培训引言
原文:SQL Server 性能调优培训引言 大家好,这是我在博客园写的第一篇博文,之所以要开这个博客,是我对MS SQL技术学习的一个兴趣记录. 作为计算机专业毕业的人,自己对技术的掌握总是觉得很肤 ...
- sql server性能调优
转自:https://www.cnblogs.com/woodytu/tag/%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98%E5%9F%B9%E8%AE%AD/defaul ...
- [转]SQL Server 性能调优(cpu)
研究cpu压力工具 perfom SQL跟踪 性能视图 cpu相关的wait event Signal wait time SOS_SCHEDULER_YIELD等待 CXPACKET等待 CME ...
- sql server 性能调优 资源等待之内存瓶颈的三种等待类型
原文:sql server 性能调优 资源等待之内存瓶颈的三种等待类型 一.概述 这篇介绍Stolen内存相关的主要三种等待类型以及对应的waittype编号,CMEMTHREAD(0x00B9),S ...
- sql server 性能调优之 资源等待 LCk
一. 概述 这次介绍实例级别资源等待LCK类型锁的等待时间,关于LCK锁的介绍可参考 “sql server 锁与事务拨云见日”.下面还是使用sys.dm_os_wait_stats 来查看,并找出 ...
- sql server 性能调优之 CPU消耗最大资源分析1 (自sqlserver服务启动以后)
一. 概述 上次在介绍性能调优中讲到了I/O的开销查看及维护,这次介绍CPU的开销及维护, 在调优方面是可以从多个维度去发现问题如I/O,CPU, 内存,锁等,不管从哪个维度去解决,都能达到调优的效 ...
- [转]SQL Server 性能调优(io)
目录 诊断磁盘io问题 常见的磁盘问题 容量替代了性能 负载隔离配置有问题 分区对齐配置有问题 总结 关于io这一块,前面的东西如磁盘大小,磁盘带宽,随机读取写入,顺序读取写入,raid选择,DA ...
- CPU开销sql server 性能调优
sql server 性能调优 CPU开销分析 一. 概述 上次在介绍性能调优中讲到了I/O的开销查看及维护,这次介绍CPU的开销及维护, 在调优方面是可以从多个维度去发现问题如I/O,CPU, 内存 ...
- 【目录】sql server 性能调优
随笔分类 - sql server 性能调优 sql server 性能调优之 资源等待之网络I/O 摘要: 一.概述 与网络I/O相关的等待的主要是ASYNC_NETWORK_IO,是指当sql s ...
随机推荐
- android file.createnewfile ioexception
近期在写项目的时候,文件有时候能创建成功有时候直接io异常,真是太扯淡.找了许久,最终找到原因 android 中创建文件,文件的名字中不能包括冒号啊这种特殊字符, 仅仅要你感觉有点特殊的字符最好都不 ...
- 网络语音视频技术浅议 Visual Studio 2010(转)
我们在开发实践中常常会涉及到网络语音视频技术.诸如即时通讯.视频会议.远程医疗.远程教育.网络监控等等,这些网络多媒体应用系统都离不开网络语音视频技术.本人才疏学浅,对于网络语音视频技术也仅仅是略知皮 ...
- DP Leetcode - Maximum Product Subarray
近期一直忙着写paper,非常久没做题,一下子把题目搞复杂了..思路理清楚了非常easy,每次仅仅需更新2个值:当前子序列最大乘积和当前子序列的最小乘积.最大乘积被更新有三种可能:当前A[i]> ...
- how tomcat works 札记(两)----------一个简单的servlet集装箱
app1 (看着眼前这章建议读者,看how tomcat works 札记(一个)----------一个简单的webserver http://blog.csdn.net/dlf123321/art ...
- 013实现使用两个堆栈队列(keep it up)
实现使用两个堆栈队列 FIFO队列是一种数据结构(FIFO),后堆叠前进出的数据结构的(FILO). 两个栈实现的最简单的方法就是排队:队列中的第一个推栈, 队列将数据顺序的第一个堆栈推入第二堆叠 ...
- Android应用程序的组成部分和Manifest文件(转)
Android应用程序由松散耦合的组件组成,并使用应用程序Manifest绑定到一起:应用程序Manifest描述了每一组件和它们之间的交互方式,还用于指定应用程序元数据.其硬件和平台要求.外部库以及 ...
- 通过java.util.concurrent写多线程程序
在JDK 1.5之前,要实现多线程的功能,得用到Thread这个类,通过这个类设计多线程程序,需要考虑性能,死锁,资源等很多因素,一句话,就是相当麻烦,而且很容易出问题.所幸的是,在JDK1.5之后, ...
- oracle从备份归档日志的方法集中回收
oracle从备份集中抓出归档日志方法 在大连医院遇到这个问题,数据库为归档状态,但归档完成后rman通过crontab自己主动备走归档日志并删除存在系统上的归档日志文件.在RealSync程序停止一 ...
- 第三十 访问财富进退自如 —Spring交易管理
6月16日本,明确. "应该留给追穷寇勇,不可沽名学霸王.天若有情天亦老,人间正道是沧桑." 有始有终.有往有还.进退自如乃Spring事务管理之道,也是万物生生不息.和谐共处之道 ...
- Json.Net6.0入门学习试水篇
原文:Json.Net6.0入门学习试水篇 前言 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.简单地说,JSON 可以将 JavaScript 对象中 ...