第六章——根据执行计划优化性能(1)——理解哈希、合并、嵌套循环连接策略

原文:第六章——根据执行计划优化性能(1)——理解哈希、合并、嵌套循环连接策略

前言:

本系列文章包括:

1、 理解Hash、Merge、Nested Loop关联策略。

2、在执行计划中发现并解决表/索引扫描。

3、 介绍并在执行计划中发现键查找并解决它们。

对于性能优化,需要集中处理以下的问题:

1、 为你的环境创建性能基线。

2、 监控现在的性能并发现瓶颈。

3、 解决瓶颈以便得到更好的性能。

一个预估执行计划是描述查询将会如何执行的一个蓝图,而一个实际执行计划就是一个查询执行时实际发生的镜像。通过对比两个执行计划,可以发现查询是否真的按照预估执行计划来执行。

在执行计划中,有一些非常重要的操作符需要清楚:

1、           Join策略:SQLServer有3种策略——哈希、合并、嵌套循环。每种策略都有其优缺点,本章将讲述这部分。

2、           扫描和查找是SQLServer用于读取数据的两种方式,这两种方式在性能优化中是核心概念。将会在下一篇中讲述。

3、           键查找有时候会成为主要的性能问题。因为存储引起必须从非聚集索引中跳到聚集索引,一边找到非聚集索引中的非键值列的值。这样的行为通常很耗时间。

理解哈希、合并、嵌套循环连接策略

SQLServer提供了3中JOIN的策略,它们没有绝对的好和坏之分。

1、 哈希(Hash Join):SQLServer选择哈希关联作为物理操作符,一边对于大容量数据,且未排序或者没有索引时进行查询。两个进程关联起来进行哈希关联,它们为【建立】和【探测】,在【建立】进程中,会从建立输入(即join的左表中,但是可能这个左表会在优化过程中交换位置,使得不一定就是实际上的左表。)读取所有行,然后在内存中创建一个符合关联条件的哈希表。在【探测】进程中,会从探测表(输入的右表)中读取所有的行,并根据关联条件,与之前创建的内存哈希表匹配。

2、 合并(Merge Join):如果关联表中已经排序,SQLServer会选择合并关联。合并关联要求关联条件中最少有一个是已经被排序了的。如果数据量不大的时候,这比哈希关联更加有效,它并不是重负荷关联的方式。

3、 嵌套循环(Nested Loop):在最少两个结果集中,使用嵌套循环会比较有效,这两个结果集中,作为外部表的集合要小,而内部循环结果集具有有效的索引。这种方式不适用于大结果集。

准备工作:

下面将创建两个表,然后看看各种关联方式的执行计划:

USE AdventureWorks
GO
IF OBJECT_ID(‘SalesOrdHeaderDemo‘) IS NOT NULL
    BEGIN
        DROP TABLE SalesOrdHeaderDemo
    END
GO

IF OBJECT_ID(‘SalesOrdDetailDemo‘) IS NOT NULL
    BEGIN
        DROP TABLE SalesOrdDetailDemo
    END
GO

SELECT  *
INTO    SalesOrdHeaderDemo
FROM    Sales.SalesOrderHeader
GO

SELECT  *
INTO    SalesOrdDetailDemo
FROM    Sales.SalesOrderDetail
GO

步骤:

1、 执行一下查询,并开启执行计划(Ctrl+M):

SELECT  sh.*
FROM    SalesOrdDetailDemo AS sd
        INNER JOIN SalesOrdHeaderDemo AS sh ON sh.salesorderID = sd.salesorderid
GO

2、 然后从执行计划截图中可以看到使用了哈希连接:

3、 现在先创建唯一的聚集索引在两个表中:

CREATE UNIQUE CLUSTERED INDEX idx_salesorderheaderdemo_SalesOrderID ON salesordheaderdemo(SalesOrderID)

GO

CREATE UNIQUE CLUSTERED INDEX idx_SalesDetail_SalesOrderID ON SalesOrdDetailDemo(SalesOrderID,SalesOrderDetailID)

GO

4、 再次执行步骤1的语句:

5、 截图是第二次执行的执行计划,可以发现变成了合并连接,并且表扫描变成了聚集索引扫描:

6、 现在来看看嵌套循环关联,在上面的查询中添加where条件来限定查询的结果集:

SELECT  sh.*
FROM    SalesOrdDetailDemo AS sd
        INNER JOIN SalesOrdHeaderDemo AS sh ON sh.salesorderID = sd.salesorderid
WHERE   sh.salesorderid = 43659
GO

7、 从执行结果中看到现在关联变成了嵌套循环:

分析:

前面已经提到,哈希关联工作在大数据量且关联字段没有排序的关联中。所以在步骤1中,由于没有索引或者预先排序,数据的关联会使用哈希关联。

在步骤3中,创建了一个唯一的聚集索引,所以表已经通过聚集索引排序了,此时优化器会选择合并关联。

在步骤6中,由于使用了where条件限制数据集的大小,同时由于已经排序,所以使用了嵌套循环关联。

每一种关联方法都有其优缺点,视乎如何优化而已。有时候哈希关联有其非常重要的作用,但是如果可以,强烈建议每个表都应该有一个唯一的聚集索引,一边使用合并关联,如果不可以,千万别尝试使用OPTION提示符来把关联改成合并或者嵌套循环,这可能会降低性能。而嵌套循环仅在小结果集的时候运行的最好。

时间: 2024-07-29 05:54:11

第六章——根据执行计划优化性能(1)——理解哈希、合并、嵌套循环连接策略的相关文章

第六章——根据执行计划优化性能(3)——键值查找

原文:第六章--根据执行计划优化性能(3)--键值查找 前言: 本文为本系列最后一篇,介绍键值查找的相关知识. 键值查找是具有聚集索引的表上的一个书签查找,键值查找用于SQLServer查询一些非键值列的数据.使用非聚集索引的查询不会有键值查找,但是所有键值查找会伴随非聚集索引出现.这里特别提醒的是键值查找总是伴有嵌套循环关联. 准备工作: 下面将创建一个表,通过执行计划看看键值查找的不同效果.为了产生键值查找,需要两件事情: 1.  聚集索引 2.  非聚集索引 当你在非聚集索引键值上有谓词时

第六章——根据执行计划优化性能(2)——查找表/索引扫描

原文:第六章--根据执行计划优化性能(2)--查找表/索引扫描 前言: 在绝大部分情况下,特别是从一个大表中返回少量数据时,表扫描或者索引扫描并不是一种高效的方式.这些必须找出来并解决它们从而提高性能,因为扫描将遍历每一行,查找符合条件的数据,然后返回结果.这种处理是相当耗时耗资源的.在性能优化过程中,一般集中于: 1.  CPU 2.  Network 3.  磁盘IO 而扫描操作会增加这三种资源的开销. 准备工作: 下面将创建两个表来查看不同的物理关联操作的不同影响.创建脚本已经在本系列的第

数据库系统实现 第六章 查询执行

第六章 查询执行 查询执行也就是操作数据库的算法 一次查询的过程: 查询-->查询编译(第七章)-->查询执行(第六章)-->数据 查询编译预览 查询编译可以分为三个步骤: a)分析:构造分析树,用来表达查询和它的结构 b)查询重写,分析树被转化为初始查询计划,通常是代数表达式,之后初始的查询计划会被优化为一个时间更小的计划 c)物理计划生成,将查询计划转化成物理的计划, 为了选择更好的查询计划,需要判断 1)查询哪一个代数的等价形式是最有效的 2)对选中形式的每一个操作,所使用的算法选

ORACLE实际执行计划与预估执行计划不一致性能优化案例

  在一台ORACLE服务器上做巡检时,使用下面SQL找出DISK_READ最高的TOP SQL分析时,分析过程中,有一条SQL语句的一些反常现象,让人觉得很奇怪: SELECT SQL_ID,        SQL_TEXT,        DISK_READS,        BUFFER_GETS,        PARSING_SCHEMA_NAME,        EXECUTIONS FROM   V$SQLAREA ORDER  BY DISK_READS DESC; 在SQL D

第六章 javaScript执行环境和作用域

这个只是点对于初学者其实大概了解就可以,但是要研究明白javaScript的机制,就是非常必要的,这只是我的一些记录,大家参考即可,如有错误请指出. 执行环境的概念是javaScript一个虚拟的概念,如何定义它呢?它的作用又是什么呢?它是怎么组成的呢? 大家都比较认可的说法:执行环境又称为执行上下文,从实际的表现来看,可以把它理解为由“对象”组成的一个堆栈.既然是堆栈,就是先入后出了. 组成堆栈的对象是什么对象?我没有找到确切的定义,基于我自己的理解,这个对象是一个自定义对象,里边包含有变量.

第六章 任务执行

6.1 在线程中执行任务 围绕任务执\执行设计应用程序结构 .讲一个复杂的功能分解为多个独立的任务. 并可以并行执行, 在调度和负载均衡过程中实现更高的灵活性. 6.1.1 串行的执行任务 在单个线程中串行的执行各项任务. 6.1.2 显示的创建任务 正常负载情况下, 为每个任务分配一个线程可以提升串行执行能力. 6.1.3 无限制创建线程的不足 线程生命周期的开销非常高. 资源消耗 . 活跃的线程会消耗系统资源 , 尤其是内存 . 可创建的线程数量存在限制. 6.2 Executor框架 任务

jdk8新特性--使用lambda表达式的延迟执行特性优化性能

使用lambda表达式的延迟加载特性对代码进行优化: 原文地址:https://www.cnblogs.com/niwotaxuexiba/p/10850339.html

[Java 并发] Java并发编程实践 思维导图 - 第六章 任务执行

根据<Java并发编程实践>一书整理的思维导图.希望能够有所帮助. 第一部分: 第二部分: 第三部分:

利用pl/sql执行计划评估SQL语句的性能简析

一段SQL代码写好以后,可以通过查看SQL的执行计划,初步预测该SQL在运行时的性能好坏,尤其是在发现某个SQL语句的效率较差时,我们可以通过查看执行计划,分析出该SQL代码的问题所在. 那么,作为开发人员,怎么样比较简单的利用执行计划评估SQL语句的性能呢?总结如下步骤供大家参考: 1. 打开熟悉的查看工具:PL/SQL Developer. 在PL/SQL Developer中写好一段SQL代码后,按F5,PL/SQL Developer会自动打开执行计划窗口,显示该SQL的执行计划. 2.