• 查看执行计划时,我们应该获得什么信息


     查看执行计划时,我们应该获得什么信息

      当你的查询很慢时,你就应该看看预估的执行计划(当然也可以查看真实的执行计划),找出耗时最多的操作,注意观察以下成本通常较高的操作:

      1、表扫描(Table Scan)

      当表没有聚集索引时就会发生,这时只要创建聚集索引或重整索引一般都可以解决问题。

      2、聚集索引扫描(Clustered Index Scan)

      有时可以认为等同于表扫描,当某列上的非聚集索引无效时会发生,这时只要创建一个非聚集索引就ok了。

      3、哈希连接(Hash Join)

      当连接两个表的列没有被索引时会发生,只需在这些列上创建索引即可。

      4、嵌套循环(Nested Loops)

      当非聚集索引不包括select查询清单的列时会发生,只需要创建覆盖索引问题即可解决。

      5、RID查找(RID Lookup)

      当你有一个非聚集索引,但相同的表上却没有聚集索引时会发生,此时数据库引擎会使用行ID查找真实的行,这时一个代价高的操作,这时只要在该表上创建聚集索引即可。

      TSQL重构真实的故事

      只有解决了实际的问题后,知识才转变为价值。当我们检查应用程序性能时,发现一个存储过程比我们预期的执行得慢得多,在生产数据库中检索一个月的销售数据居然要50秒,下面就是这个存储过程的执行语句:

      exec uspGetSalesInfoForDateRange ‘1/1/2009’, 31/12/2009,’Cap’

      Tom受命来优化这个存储过程,下面是这个存储过程的代码:

     ALTER PROCEDURE uspGetSalesInfoForDateRange

      
    @startYear DateTime,

      
    @endYear DateTime,

      
    @keyword nvarchar(50)

      
    AS

      
    BEGIN

      
    SET NOCOUNT ON;

      
    SELECT

      Name,

      ProductNumber,

      ProductRates.CurrentProductRate Rate,

      ProductRates.CurrentDiscount Discount,

      OrderQty Qty,

      dbo.ufnGetLineTotal(SalesOrderDetailID) Total,

      OrderDate,

      DetailedDescription

      
    FROM

      Products 
    INNER JOIN OrderDetails

      
    ON Products.ProductID = OrderDetails.ProductID

      
    INNER JOIN Orders

      
    ON Orders.SalesOrderID = OrderDetails.SalesOrderID

      
    INNER JOIN ProductRates

      
    ON

      Products.ProductID 
    = ProductRates.ProductID

      
    WHERE

      OrderDate 
    between @startYear and @endYear

      
    AND

      (

      ProductName 
    LIKE '' + @keyword + ' %' OR

      ProductName 
    LIKE '' + @keyword + ' ' + '%' OR

      ProductName 
    LIKE '' + @keyword + '%' OR

      Keyword 
    LIKE '' + @keyword + ' %' OR

      Keyword 
    LIKE '' + @keyword + ' ' + '%' OR

      Keyword 
    LIKE '' + @keyword + '%'

      )

      
    ORDER BY

      ProductName

      
    END

      
    GO

    分析索引

      首先,Tom想到了审查这个存储过程使用到的表的索引,很快他发现下面两列的索引无故丢失了:

      OrderDetails.ProductID

      OrderDetails.SalesOrderID

      他在这两个列上创建了非聚集索引,然后再执行存储过程:

      exec uspGetSalesInfoForDateRange ‘1/1/2009’, 31/12/2009 with recompile

      性能有所改变,但仍然低于预期(这次花了35秒),注意这里的with recompile子句告诉SQL Server引擎重新编译存储过程,重新生成执行计划,以利用新创建的索引。

      分析查询执行计划

      Tom接下来查看了SQL Server Management Studio中的执行计划,通过分析,他找到了某些重要的线索:

      1、发生了一次表扫描,即使该表已经正确设置了索引,而表扫描占据了总体查询执行时间的30%;

      2、发生了一个嵌套循环连接。

      Tom想知道是否有索引碎片,因为所有索引配置都是正确的,通过TSQL他知道了有两个索引都产生了碎片,很快他重组了这两个索引,于是表扫描消失了,现在执行存储过程的时间减少到25秒了。

      为了消除嵌套循环连接,他又在表上创建了覆盖索引,时间进一步减少到23秒。

      实施最佳实践

      Tom发现有个UDF有问题,代码如下: 

    ALTER FUNCTION [dbo].[ufnGetLineTotal]

      (

      
    @SalesOrderDetailID int

      )

      
    RETURNS money

      
    AS

      
    BEGIN

      
    DECLARE @CurrentProductRate money

      
    DECLARE @CurrentDiscount money

      
    DECLARE @Qty int

      
    SELECT

      
    @CurrentProductRate = ProductRates.CurrentProductRate,

      
    @CurrentDiscount = ProductRates.CurrentDiscount,

      
    @Qty = OrderQty

      
    FROM

      ProductRates 
    INNER JOIN OrderDetails ON

      OrderDetails.ProductID 
    = ProductRates.ProductID

      
    WHERE

      OrderDetails.SalesOrderDetailID 
    = @SalesOrderDetailID

      
    RETURN (@CurrentProductRate-@CurrentDiscount)*@Qty

      
    END

      在计算订单总金额时看起来代码很程序化,Tom决定在UDF的SQL中使用内联SQL。

      dbo.ufnGetLineTotal(SalesOrderDetailID) Total -- 旧代码

      (CurrentProductRate-CurrentDiscount)*OrderQty Total -- 新代码

      执行时间一下子减少到14秒了。

      在select查询清单中放弃不必要的Text列

      为了进一步提升性能,Tom决定检查一下select查询清单中使用的列,很快他发现有一个Products.DetailedDescription列是Text类型,通过对应用程序代码的走查,Tom发现其实这一列的数据并不会立即用到,于是他将这一列从select查询清单中取消掉,时间一下子从14秒减少到6秒,于是Tom决定使用一个存储过程应用延迟加载策略加载这个Text列。

      最后Tom还是不死心,认为6秒也无法接受,于是他再次仔细检查了SQL代码,他发现了一个like子句,经过反复研究他认为这个like搜索完全可以用全文搜索替换,最后他用全文搜索替换了like搜索,时间一下子降低到1秒,至此Tom认为调优应该暂时结束了。

  • 相关阅读:
    PhpExcel笔记,phpExcel中文帮助手册
    mysql “group by ”与"order by"的研究--分类中最新的内容
    mysql中,主键与普通索引
    mysql性能优化-慢查询分析、优化索引和配置
    OpenSSL
    HAProxy
    Lighttpd
    Linux find/grep命令
    keepalived
    iptables
  • 原文地址:https://www.cnblogs.com/wangyhua/p/4050546.html
Copyright © 2020-2023  润新知