SQL Server中的执行引擎入门 图解
当我们使用数据库查询时,查询优化器在幕后为我们做了大量的工作,为我们选择最优的执行计划。执行计划中的每一个操作都是经过精心设计的,旨在以最高效的方式获取数据。
最基本的操作是数据访问。数据的组织方式可以分为堆和B树。当表没有建立聚集索引时,数据以堆的形式组织,这是一种无序的方式。一旦为表创建了聚集索引,数据则以B树的形式有序存储。非聚集索引也采用B树结构。
数据访问有两种主要方式:扫描和查找。扫描意味着查看结构的所有内容,而查找则只查看部分数据。由于堆的无序性,我们无法在堆上进行查找操作。而对于B树,由于其有序结构,查找操作变得高效可行。
当我们对一个以堆组织的表进行访问时,会进行所谓的表扫描,即从头到尾查看整个表。而对于聚集索引和非聚集索引的B树结构,同样可以进行扫描。当需要获取索引表中的所有数据或当扫描的成本低于查找时,会进行聚集索引扫描。
而对于仅仅选择B树结构中的部分数据,索引查找使得B树的价值得到体现。通过查找关键值,我们可以从B树的根部快速定位到所需的数据页,避免了扫描不必要页的消耗。
当非聚集索引不包含所有所需列时,SQL Server需要进行书签查找。这意味着通过非聚集索引找到所需行后,还需要到基本表中找到非聚集索引未覆盖的列。这个过程被称为书签查找,有时它可能不如直接扫描高效。
聚合操作是数据库查询中的另一个重要环节。聚合函数可以将一个集合的数据按照特定规则汇总成单个或多个数据点。这些操作在处理大量数据时尤为关键,因为它们可以帮助我们有效地从大量数据中提取有意义的信息。
数据库查询的执行计划是一个复杂的系统,涉及到数据的组织、访问、查找和聚合等多个环节。理解这些操作有助于我们更好地优化查询,提高数据库的性能和效率。对于聚合函数如avg、sum、min等以及distinct关键字,它们在数据库中的运作会引发两类主要的聚合操作:流聚合(Stream Aggregation)和哈希聚合(Hash Aggregation)。
流聚合(Stream Aggregation)
流聚合处理的数据集合需要是有序的,这一有序性可以通过执行计划中的排序操作实现,也可以直接从聚集或非聚集索引中获取。在没有Group by的聚合操作中,称为标量聚合,这类操作一定会执行流聚合。
以标量聚合为例,其执行过程如图7所示。在这种情况下,数据按照某一顺序流动,然后依次进行聚合操作。
当操作中加入Group by子句时,数据需要按照Group by后面的列进行排序。这种排序操作占用内存,如果内存不足,还会占用tempdb。SQL Server会在排序操作和散列匹配中选择成本最低的方法。这一过程如图8所示。
哈希聚合(Hash Aggregation)
对于较大的数据表,使用哈希集合进行聚合的成本通常低于排序。哈希聚合通过在内存中建立哈希表来实现,无需对数据集合进行排序。内存中的哈希表以Group by后面的列为键,相应的数据为值。这一过程如图9所示。
在内存中建立好哈希表后,会根据Group by后面的值(作为键)依次处理集合中的每条数据。当键在哈希表中不存在时,会向哈希表添加条目;当键已存在时,则根据聚合函数(如Sum、Avg等)计算哈希表中的值。
连接(Join)
在数据库查询中,当需要连接多个表时,SQL Server会采用不同类型的连接方式,包括循环嵌套连接(Nested Loops Join)、合并连接(Merge Join)和散列连接(Hash Join)。选择哪种连接方式取决于具体的数据和查询场景。
循环嵌套连接(Nested Loops Join)
循环嵌套连接是一种常见的数据连接方式,特别是在其中一个表的行数相对较少,而另一个表的行数非常多的情况下。这个过程可以简单地理解为,外部输入(通常是扫描操作)对每一行数据进行处理,然后针对每一行数据在内部输入(通常是查找操作)中查找匹配的行。这个过程如图10所示。由于外部输入是扫描操作,而内部输入是查找操作,当外部输入的结果集较小而内部输入的表非常大时,查询优化器通常会选择循环嵌套连接方式。这一过程的详细执行情况和内部输入的执行次数如图11所示。
合并连接(Merge Join)
与循环嵌套连接不同,合并连接从每个表只执行一次访问。它适用于两个输入都已排序的情况,并且Join的条件通常为等于号。合并连接的工作原理是,从每个输入流中取出一行进行比较,如果满足条件则返回结果,否则舍弃。这要求输入数据是有序的,如果无序,查询分析器不会选择合并连接。可以通过索引提示强制使用合并连接,但这需要在执行计划中加入排序步骤来实现数据的有序化,如图13所示。
数据库在处理聚合操作和表连接时,会根据数据的特性、查询的场景以及成本最低的原则选择合适的操作方法。深入理解SQL Server中的连接策略:合并连接、散列连接与并行处理
在数据库查询中,连接操作是极其常见的,特别是在处理多表查询时。SQL Server在处理这些连接操作时,采用了多种策略,包括合并连接、散列连接以及并行处理。本文将带你深入理解这些操作及其背后的原理。
一、合并连接(Merge Join)
合并连接是一种通过在两个数据集上执行排序操作来匹配行的连接方式。当数据已经排序或可以容易地进行排序时,通常会选择这种连接方式。通过排序,它可以在两个数据集之间进行比较,找到匹配的行。这个过程就像将两个已排序的列表合并成一个。图13展示了通过排序实现Merge Join的过程。合并连接需要额外的排序操作,这可能会增加查询的复杂性和时间。但如果数据已经是有序的,或者数据库管理系统可以有效地进行排序,那么合并连接可以是一个高效的选择。
二、散列连接(Hash Join)
与合并连接不同,散列连接通过在内存中建立散列表来实现连接操作。这种连接方式不需要数据预先排序。它通过生成一个哈希表来存储一个数据集的数据,并用另一个数据集的数据来查找匹配的行。这个过程类似于在电话簿中查找信息,不需要对电话簿进行排序。图14展示了散列连接的过程。散列连接比较消耗内存,如果内存不足,还会占用tempdb。当数据量大且无需预先排序时,散列连接可能是一个更好的选择。当一方或双方的数据未排序时,散列连接也是更好的选择。在图15中,可以看到散列键的生成和探测过程。
三、并行处理
当需要处理大量数据或多表连接时,SQL Server允许查询并行处理。这意味着查询可以在多个CPU或多核上同时执行,从而提高效率。并行处理可以显著加快大型查询的执行速度,特别是在多核系统或集群环境中。图16展示了并行处理提高效率的示例。
了解SQL Server执行计划中的这些常见操作及其原理是优化查询的基本功。根据数据的特性和查询的需求,选择合适的连接方式以及利用并行处理可以显著提高查询的效率。通过理解这些步骤和原理,数据库管理员和开发者可以更好地优化他们的查询,从而提高数据库的性能和响应速度。