前言html
前面几篇咱们分析了关于SQL Server关于性能调优的一系列内容,我把它分为两个模块。算法
第一个模块注重基础内容的掌握,共分7篇文章完成,内容涵盖一系列基础运算算法,详细分析了如何查看执行计划、掌握执行计划优化点,并一一列举了平常咱们日常所写的T-SQL语句所会应用的运算符。我相信你日常所写的T-SQL语句在这几篇文章中都能找到相应的分解运算符。数据库
第二个模块注重SQL Server执行T-SQL语句的时候一些内幕解析,共分为5篇文章完成,其中包括:查询优化器的运行方式、运行时几个优化指标值检测,统计信息、利用索引等一系列内容。经过这块内容让咱们了解SQL Server为咱们所写的T-SQL语句如何进行优化及运行的。并发
从本篇进入第三个模块的内容,该篇为第一篇,该模块主要让咱们来指导SQL Server进行定向调整,达到优化的目的。本模块的内容是之前面一系列内容为前提的,但愿充分掌握了前面基础内容,方能进入本模块内容。函数
技术准备高并发
数据库版本为SQL Server2012,利用微软的之前的案例库(Northwind)进行分析,部份内容也会应用微软的另外一个案例库AdventureWorks。工具
相信了解SQL Server的朋友,对这两个库都不会太陌生。post
概念理解性能
谈到hint,其实概念很简单,正如词义理解:提示,也就是说让咱们经过给予SQL Server提示(hint)让数据库运行时按照咱们的思路进行,我估计不少不怎么了解SQL Server的童鞋都不怎么知道,由于通常应用的很少。学习
其实,SQL Server自己的查询优化器已经作到很好了,因此大部分状况下不须要咱们人工干预,本身就能运行的很好,而且最大限度的优化运行项。可是,俗话说:老虎也有打盹的时候,因此,在有些场景下,就须要咱们来给数据库指导一个方向,让其运行的更流畅。
可是,记住了:你所应用的hint是在如今的场景中基于现有的环境下,相对是一个好的方式,不能确保你所给予的提示(Hint)永久有效,而且随着时间推移,数据量的变动,你所发出的提示(Hint)有可能会成为数据库优化的绊脚石。因此没有充分的把握不要轻易使用Hint,而且最好采用目标导向Hint。
Hint主要分为三类应用:查询Hint、表Hint、链接Hint。查询Hint影响整个查询,主要应用于查询语句优化,本篇主要分析查询Hint。
表Hint影响查询引用的单个表,而链接Hint影响一个单独的链接。
Hint应用方式分为两类:目标导向Hint和物理运算符Hint。
目标导向Hint传递逻辑的目标给优化器,而不会具体指定优化器应该如何达到这个目标,应该使用什么物理运算符,或者如何排列这些运算符。因此这种运算符使咱们所推荐的,缘由很简单:我告诉丫按照这个思路执行就能够,至于怎么达到,本身想办法!这种方式从长期看对于数据库的影响会小不少。
另一个就是物理运算符,此方式就更直接了:直接告诉丫的步骤,你按照这个去作就行。这种方式不推荐,缘由很简单:你的思路暂时会是好的,可是过段时间就很差了。
1、查询提示(Hint)
首先,查询提示(Hint)是咱们在调优中应用最普遍的,由于大部分时间咱们是在调整查询的性能。
关于查询中的优化选项就是在指导SQL Server的链接类型、聚合类型、联合类型等物理链接运算符。关于此块的详细解析,能够参照我调优系列中前几篇文章,分析的至关的详细。
a、FAST N Hint提示
关于此方式的提示,我在前面的文章中已经有使用到,在介绍索引那篇文章中,能够点击这里查看。
首先,这个Hint是一个目标导向hint。提示目标很简单:告诉数据库给我速度出前N行数据就能够,而其它的数据你爱咋地咋地。
这个提示最优的应用环境就是:应用系统中的分页查询,固然其它环境能够用。有点相似于SELECT TOP N....
其次,在咱们的应用环境中,尤为数据量多的状况下,若是这时候咱们的场景是:我想速度的看到前面的部分数据,其它的数据你能够稍后再显示,可是在执行T-SQL的时候,SQL Server会多方面的考虑耗费(cost),而后再平衡各类利弊选择出它认为相对好的执行计划去执行,显然这种方式获取数据的方式是很浪费的,而且速度就会相对慢不少。
因此,咱们利用FAST N Hint提示,这样,SQL Server会阻止优化器使用哈希链接、哈希聚合、排序、甚至是并行这些大消耗的动做,而转变成为这N条数据作快速的优化并输出。这在大数据量的状况下,是一种很是高明的方式。
来个例子:
SELECT OrderID,CustomerID,OrderDate FROM Orders ORDER BY OrderDate
简单的查询,而且按照OrderDate排序,不看执行计划,咱们就已经推测出这个执行计划中最耗损的就是这个OrderDate了,排序永远是高耗损,这也是为何各类类型的索引都要提早排序的缘由。
而后,咱们再来看一下加上这个FAST N Hint提示的执行
SELECT OrderID,CustomerID,OrderDate FROM Orders ORDER BY OrderDate OPTION(FAST 1)
为了快速获取这一行数据,利用HINT后,改成了索引扫描+书签查找,由于这是获取一条数据的最优的一种方式。
由于数据量的关系,因此我上述演示没能很好的表现出FAST 提示的优越性来,其实在实际生产中,在面临庞大的数据量的时候,通常利用FAST N提示获取出部分数据以后,就再也不继续运行了,由于咱们关注的就是这一部分数据。
固然,此HINT也有弊端:在快速获取前N行结果以后,可能会延迟整个查询的整体相应时间。也就是说,尽管FAST N HINT可能会使优化器快速产生前N个输出计划。可是它会使优化器产生一个在结束最后一行前花费更多时间,消耗更多CPU,甚至于更多IO。
b、OPTIMIZE FOR Hint提示
此HINT是一种很是有用的提示,也是咱们在平常中常用的。
这个HINT目标很简单:告诉优化器目标以Hint值进行分配或者执行。此Hint提示是从SQL Server2005版本以上开始支持,可以根据指定的参数值产生一个计划,尤为适用于非对称数据集中,由于这种数据集中数据分布不均匀,不一样的参数值可能致使不一样的基数评估和不一样的查询计划,咱们能够从不一样的参数中选择一个最优的执行计划,做为后续不一样参数的执行计划,避免了SQL Server的从新评估和重编译的耗费的动做。
来个例子:
SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=N'51100'
此语句很简单,就是经过查询邮政编码(ShipPostalCode),获取出订单ID和订单日期。
来看这个查询语句,最理想的状况就是直接经过索引查找(index seek)动做获取出数据。其实最好的方式也是经过INCLUDE将两列值包含进去。
咱们来看一下实际的执行计划:
SQL Server经过了索引查找+书签查找方式获取,这种方式也凑合吧,其实咱们还能够继续优化。
可是,这不是问题重点,问题重点是该段T-SQL通常咱们会利用参数进行查询或者包装成存储过程经过传参调用。是吧??不会你永远只查询一个固定值吧....来看语句
DECLARE @ShipPostalCode NVARCHAR(50) SET @ShipPostalCode=N'51100' SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=@ShipPostalCode
是吧,这种方式才能作到重用嘛,不过包装成一个存储过程或者一个函数等,估计核心代码确定就这样子了。
来看看生成的执行计划:
原本很爽的非汇集索引查找(Seek),经过我加了一个参数以后变成了汇集索引扫描(Scan)了,汇集索引扫描的性能跟表扫描基本同样,没有啥质的提升!
若是该表数据量特别大的话,咱们为该语句设计的非汇集索引就失效了。只能经过依次扫描获取数据了。有意思吗???没意思!!!
怎么解决呢?这就是咱们此处提到Hint出场的时候了,告诉数据库:丫就按照执行 “51100” 的查询同样去执行我传过来的参数。
DECLARE @ShipPostalCode NVARCHAR(50) SET @ShipPostalCode=N'51100' SELECT OrderID,OrderDate FROM Orders WHERE ShipPostalCode=@ShipPostalCode OPTION(OPTIMIZE FOR( @ShipPostalCode=N'51100'))
看到了,这里又回归了快速的非汇集索引查找(Seek)状态,而且不受限制于传过来的参数是啥。
这个提示只是告诉SQL Server查询按照这个目标值进行操做,并不会实际影响结果值。
固然上面的问题,若是封装成存储过程的时候,能够采用重编译的方式解决,可是相比利用Hint的方式,重编译带来的消耗远大的多。尤为高并发的环境下重编译所带来CPU消耗是很是高的。
c、物理链接提示(Hint)
关于物理链接咱们在前面的文章中已经详细的分析了,在SQL Server中共分为三种物理链接方式:嵌套循环、合并、哈希链接。
详细的内容能够参照个人基础篇中的连接:SQL Server调优系列基础篇(经常使用运算符总结——三种物理链接方式剖析)
文章中对三种链接的利弊进行了详细的对比,而且对三种链接的使用环境进行了详细的介绍。可是,有时候SQL Server为咱们评估的链接并非最优的,或者说并非符合咱们的要求,这时候,就要利用咱们的物理链接提示进行指导。
总共分为三种查询级别的链接Hint,正好对应三种物理链接运算符,依次是:LOOP JOIN、MERGE JOIN 和 HASH JOIN
在应用时候,能够指定一个或者多个,若是指定一个,那么查询计划中的所有链接使用指定的链接类型,若是指定两个,SQL Server会在这两个链接类型中选择最好的一个,也就是毙掉了第三个。
应用场景蛮多的,根据三种链接的特性,咱们能够有选择的进行提示,好比咱们想一个查询不消耗内存,那么就能够指定OPTION(LOOP JOIN,MERGER JOIN),这样就去掉消耗内存的哈希链接,固然这是减少内存消耗但会增长执行时间。若是采用了合并链接(MERGER JOIN)方式不会消耗内存,可是合并链接须要提早排序(sort),排序会消耗大量的内存。
固然,有时候嵌套循环链接执行的时间不理想,就能够指定为哈希链接(hash join)进行链接。
来看个例子:
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London'
上面的查询计划采用了嵌套循环的链接方式,两张表依次进行循环嵌套执行。
若是,通过测试这里发现采用合并链接的方式更好一点,咱们能够采用以下Hint进行提示操做
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London' OPTION(MERGE JOIN)
通过调整以后,这时候该语句就利用到了咱们设计的非汇集索引,而且由原来的索引SCAN变成了索引Seek运算。
经过以下方式,能够指导SQL Server在哈希链接和合并链接之间作出选择,可是必定要放弃嵌套循环链接。
SELECT o.OrderID FROM Customers C JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London' OPTION(HASH JOIN,MERGE JOIN)
看以看到,通过评估SQL Server仍是依然的选择了合并链接
其实,这个很正常,首先数据量不大,其次是在City列上存在非汇集索引,因此要充分利用,而且在两张表的CustomerID是都为索引所覆盖,这就保证了两张表在这列上都是预先排序(sort)了,这彻底知足了合并链接的条件。固然,默认选择嵌套循环链接的缘由,我估计的缘由就一个:两张表数据量不大。
固然,出来上面的HINT方式能够指定链接的物理链接方式,还有另外更为粗暴的一种方式,强制执行。以下:
SELECT o.OrderID FROM Customers C INNER MERGE JOIN Orders O ON C.CustomerID=O.CustomerID WHERE C.City=N'London'
固然,这种方式也手动的达到了指定采用合并链接的方式。
可是,此种方式有严重的弊端:
一、经过采用这种方式貌似暂时解决问题了,可是通过一段时间,此链接方式可能会严重阻碍数据库的优化,而要解决此问题就不得不更改代码。
二、只能粗暴的指定一种物理链接方式,不能顺应SQL Server自己本身的优化策略。
上述的方式是很是不推荐的一种,大部分新手会选择这种方式。
固然,利用Hint的方式是并不是一种万全之策,但在当前基本能解决问题,当运行到一段周期以后,若是当前的HINT干预了SQL Server数据库的正常运行,咱们也能够采用适当的方式予以停用Hint。使数据库获得完美的平稳的正常运行。后续文章咱们依次介绍。
关于Hint这块的使用,内容仍是挺多的,其中一部分还包含锁提示等,后续文章咱们依次介绍,有兴趣的童鞋提早关注。
其实Hint是日常咱们调优时候一种重要的工具。可是,这个工具的正确的使用则要依靠牢靠的基础知识掌握和经验累积。正所谓:厚积薄发! 不要轻易的看到了使用场景就妄自的进行盲目的使用。若是使用不当,还会扰乱SQL Server数据库自己正常的生态环境,得不偿失,越调越乱。
因此:施主,三思而行呀......
参考文献
结语
此篇文章先到此吧,关于SQL Server调优工具Hint的使用还有不少内容,后续依次介绍,有兴趣的童鞋能够提早关注。
有问题能够留言或者私信,随时恭候有兴趣的童鞋加入SQL SERVER的深刻研究。共同窗习,一块儿进步。
文章最后给出前面几篇的链接,如下内容基本涵盖咱们平常中所写的查询运算的分解以及调优内容项,皆为原创,看来有必要整理一篇目录了.....
-----------------如下进阶篇-------------------