seq搜索引擎优化至少包括那几步?(简单地写出一个存储过程实现businees功能并不是一件,难的是如何写出)
优采云 发布时间: 2021-10-26 06:04seq搜索引擎优化至少包括那几步?(简单地写出一个存储过程实现businees功能并不是一件,难的是如何写出)
众所周知,SQL Server 是我们常用的关系型数据库之一。简单写一个存储过程来实现businees功能并不难。难点在于如何编写一个高效的存储过程来快速得到我们想要的结果。这就是我们通常所说的高性能存储过程,或者说性能优化。在具体说明之前,我们先来看看一个查询的执行过程。
查询流程
SQL Server 数据库引擎由两个主要部分组成,一是存储引擎,二是查询过程。前者主要负责读取磁盘和内存中的数据,保证数据的一致性。后者,顾名思义,主要是接受查询,设计一个优化的计划,然后执行计划,最后返回结果。
整个查询过程中所做的事情可以用下图表示:
当我们收到查询语句时,首先要解析查询语句,主要是看他要查哪个表,做了什么样的join。这一步也至少保证了我们收到的查询可以被解析,否则我们不知道该怎么办。这里的检查更多是语法检查。
解析完成后,进行绑定。这一步更多的是一个名称对应的过程,就是将上一步解析出来的内容与我们系统中实际存在的一些对象进行对应,顺便检查一下这些对象是否实际存在。从而进一步验证查询的有效性,当然这里的检查更多的是逻辑检查。
绑定后,我们需要找到一个最优的执行计划,这里会列出一些可能的执行计划,然后找到最优的执行计划。然而,现实并不总是如我们预期的那样。例如,一条查询语句可能有数万或数十万设置数百万个执行计划。我们不可能遍历所有的执行计划并找到最优执行。计划。因为这个遍历也需要时间,所以这里会有一个折衷。因为我们只能说,这里我们会找到一个局部最优解(想想AI,哈哈)来设置执行计划。
有了查询优化的结果,后面两步我们就不用动太多脑子了,直接执行这个查询,返回结果即可。
了解了整个流程后,我们知道其实所有的关键都是查询优化。为了选择这一步最优的执行计划,我们首先要看看它会产生哪些执行计划,然后分析这些执行计划在我们的场景中是好是坏,或者我们场景中哪些步骤占用了较大的时间块,以便我们提供具体的优化建议。因此,本文主要介绍SQL Server中的执行计划。
前期准备
这篇文章有很多测试来说明我们的解释,所以你需要准备一些软件和数据
SQL Server,我安装了 SQL Server 2017
SQL Server 示例数据:本文使用 AdventureWorks-oltp-install-script.zip 进行测试。
数据访问操作
首先,我们来看一下数据访问的操作。所谓数据访问,就是直接访问数据,可以是表访问,也可以是索引访问。通常有两种方法:一种是扫描,另一种是搜索。扫描就是读取整个结构,可以访问堆或者聚集索引或者非聚集索引。搜索不会读取整个结构,通过索引访问一行效率更高,所以从这个角度来看,搜索只能应用于索引。下表显示了简要摘要:
让我们来看看几个扫描示例。在开始具体的例子之前,先解释一下如何获取执行计划。其实获取执行计划的方法有很多。最简单的方法之一是在 SSMS 中打开“收录实际执行计划”。'选项,如下图所示:
全表扫描
所以我们可以看到如下的执行计划:
我们可以清楚地看到这是一次全表扫描。
聚集索引扫描
所以我们可以看到如下的执行计划:
这里要强调的一点是,虽然我们的聚集索引在保存时是有序的,但并不能保证我们通过他扫描的结果也是有序的,所以如果你想按照聚集索引进行排序,结果请明确添加order by语句,如下图:
此时,将鼠标悬停在聚集索引扫描图标上,可以看到如下结果:
这里我们可以看到,弹出的属性窗口中的ordered属性设置为true,这说明我们的结果是有序的,如果你回去看之前的属性,毫无疑问是false。
非聚集索引扫描
我们可以看到下面的结果
在这个例子中,我们查询addressId、city和stateProvinceId三列,查询IX_AddressLine1_AddressLine2_City_StateProvinceID_PostalCode。此索引是根据 AddressLine1、AddressLine2、City、StateProvinceID 和 PostalCode 列生成的。显然 city 和 stateProvinceId 在这个索引中,但是 addressID 不在其中,那么我们如何得到这个列呢?其实原因很简单,addressId是一个聚簇索引,所有非聚簇索引默认都收录聚簇索引列,所以我们在设置非聚簇索引时不需要显式收录聚簇索引列。
上面提到的三个扫描将扫描所有结构。让我们看一些查询示例:
聚集索引查询
结果如下图所示:
非聚集索引查询
结果如下图所示:
对于聚集索引查询和非聚集索引查询,它们是相似的。唯一的区别是前者可以覆盖所有列,而后者只能覆盖特定列。原因也很简单,聚簇索引在逻辑上,是按照聚簇键排序的。可惜一张表只能有一个聚集索引。
书签查找
在上一节的最后,我们说非聚集索引查询不能覆盖所有列。如果我们查询的列有的在这个非聚集索引中,有的不在,这时候我们该怎么办?有两种可能的选择,一种是先扫描非聚集索引,然后再查询表得到其他列,另一种是直接扫描表。这两种情况都有可能,具体情况可能需要具体分析。
使用方法1的例子:
结果如下图所示:
这里因为City和ModifiedDate不在非聚集索引中,所以我们选择先用这个索引来查找,然后key查找整个表。这里上面的索引查找只会执行一次,然后接下来的键查找会执行n次,其中n是我们在索引查找中找到的行数。将鼠标放在对应的执行计划上,我们可以看到执行次数分别为1(非聚集索引查找)和25(键查找)。
使用方法二的例子:
对于同一个查询语句,不同的查询值可能有不同的执行计划。这里我们将 stateProviceID=1 改为 stateProviceID=20
执行结果如下:
我们可以看到这里选择了对基本表的扫描,而不是先扫描非聚集索引。其实对于这个简单的例子,主要是和输出行数有关。不难想象,如果有足够多的输出函数,那我们不妨直接扫描整个表。您可以试验看看输出行数的临界值会影响最有效的执行计划。
在上面的例子中,我们有一个聚集索引,有时我们会执行堆查找。看下面的例子:
结果如下:
至此,我们执行计划的第一部分:数据访问操作和书签查询就完成了。我们将在后续文章中继续介绍其他执行计划的内容,敬请期待。