同时,algebrizer组件还负责确定解析树中的每个节点的类型是否和数据库中对应的是否一致。algebrizer组件以从下到上的方式开始遍历树,即,先从页级节点开始,也就是列和常量。
绑定解析是一个非常重要的过程,在这个过程中还会识别出我们自己定义的一些别名。这个过程执行完成之后,就会产生一个二进制的“查询处理树”,这个树会被传递给查询优化器。
查询优化器
查询优化器使用查询处理树和相关的统计信息来生成一个执行计划。
换句话说,查询优化器指出了如何最好的去执行提交的sql语句。查询优化器会决定是否可以采用索引来访问数据,采用那种类型的join操作会更好(例如,尽管我们有时候在sql中写的是Left Join,可能查询优化器在分析之后,在保证结果一样的前提下,采用Inner Join)。
查询优化器是一个基本成本分析的优化器。这意味着它会尝试为每个sql语句生成成本最低的执行计划。
另外,我们来归对于优化器所用到的统计数据进行简要的解析。所谓的统计数据,就是在数据库中描述列、索引相关信息的数据,即数据的数据,或称之为“元数据”。优化器就是结合统计数据和查询处理树来进行成本的估计的。
在默认的情况下,统计信息是由数据库内部自动的进行更新的(在调优的时候,可以手动的更新)。
需要提及的就是:表变量是没有任何的统计数据的,也就是说,如果对表变量中的数据进行查询,优化器是不做任何的优化的。但是临时表是有相应的统计数据的。
有一点需要注意的就是:上面的成本只是“估算”而已。一些复杂的语句可能会有很多个候选的执行计划,在这种情况下,查询优化器不会分析所有的组合,而是找出一个接近理论最小值的一个执行计划。计划的成本表现为估计完成查询所需的时间。最低估计成本不一定是最低的资源成本。
查询执行
一旦执行计划生成之后,操作就转入存储引擎中,这也是查询真正被执行的地方,也是根据估计执行计划 产生实际执行计划的产所。
查询计划的重用
从之前的一些步骤可以看到:Sql Server产生一个实际的执行计划需要很多的步骤和很多的成本(执行计划的过度编译往往成为一个很大的性能问题),必须尽可能的重用执行计划(如果后文不做特殊说明,执行计划就指代“实际执行计划”),所以,在数据库中,一旦执行计划产生之后,就被缓存在了内存中(称之为计划缓冲)。
正如之前所提到的,当优化器产生了估计的执行计划之后,计划就会被传递给存储引擎。其实在将估计的执行计划传给存储引擎之前,查询优化器就去“计划缓冲”中查找与现在估计的执行计划对应的实际执行计划。如果找到了,那么,查询优化器将会使用执行计划传进行后续操作。这样就避免了重新生成实际的执行计划。
一般而言,每个查询的执行计划都只保存一个,除非查询优化器知道采用并行执行可以产生更好的性能,此时,并行查询的执行计划就被缓存起来,也就是说:同一个查询,在计划缓冲中有两个执行计划。
执行计划并不是永远被保存在内存中的。它们也是会过期的。SQL Server会基于最近最少使用的算法来移除那些不常用的执行计划。下面列出了执行计划被移除的几个条件:
1、系统产生了内存压力,需要更多的内存,此时迫使SQL Server释放自己占用的内存。
2、内存中的执行计划的最近使用次数为0.
3、执行计划没有被现在的数据库连接引用。
注:熟悉.NET的朋友,可以将之与.NET的垃圾回收机制类比理解。
今天就到这里,下一篇,我们将对执行计划进行更多的分析!