查询优化器内核剖析第三篇:查询的执行与计划的缓存 & Hint提示
查询优化器内核剖析第三篇:查询的执行与计划的缓存 & Hint提示
查询优化器内核剖析第二篇:产生候选执行计划&执行计划成本估算
查询优化器内核剖析第三篇:查询的执行与计划的缓存 & Hint提示
查询优化器内核剖析第六篇:谈谈Join的顺序问题,纠正江湖偏方
查询优化器内核剖析第七篇:执行引擎之数据访问操作---Scan
查询优化器内核剖析第八篇:执行引擎之数据访问操作---Seek与Lookup
查询优化器内核剖析第九篇:执行引擎之数据访问操作---Bookmark Lookup
查询优化器内核剖析第十篇:执行引擎之数据访问操作- Aggregations(聚合)
本篇议题如下:
查询的执行与计划的缓存
Hint提示
首先看到第一个议题
查询的执行与计划的缓存
一旦查询被优化之后,存储引擎就使用选中的执行计划将结果返回,而被使用的这个执行计划就会被保存在内存中一个被称之为“计划缓存”的地方,从而使得这个执行计划可以被重用,从而节省CPU等资源。
尽管我们可以把执行计划缓存起来,便于重用,但是在某些情况,对于某些查询而言,计划重用并不是很好的选择。这是为什么?这主要取决于表中数据的分布情况和查询中所使用的参数,其实这种情况也被称为“参数嗅探(Parameter Sniffing,翻译过来还是很别扭的)”。
这里,我们就简单的说一下(详细的后续文章会慢慢的介绍)。例如,假设有这样一个查询语句,如下图所示:
执行如下:
这个时候,存储过程运行,产生了一个执行计划,并且被缓存起来了。
之后,又要运行这个查询,但是传入的参数不同,如下:
这个时候,这个查询可以使用之前创建的执行计划,但是这个时候也很有可能之前的执行计划对与参数870不是最优化的。因为之前在生成的执行计划的时候,查询优化器是通过传入的897创建的执行计划。
试想:如果在表中的数据,有很多的ProductId的值都为897,即使我们在ProductId上面建立了索引,但是还有可能查询优化器决定在执行计划会采用扫描整表来获取数据。而对于参数值为870的时候,在表中的存在相同的ProductId为870的数据很少,这个时候,如果采用索引查找,可能会更快,那么就说明之前的执行计划中的整表扫描不适合了!
当然,这里只是简要的说明了一下,大家可能不是非常的明白,没关系,只要知道有这么个情况就行了,我们在后面会详细剖析。
有时候,即使执行计划已经在计划缓存中存在了,但是有可能随着一些元数据的改变(修改表的结构,移除索引等操作)会导致执行计划失效,或者不是比较优化的,或者甚至从计划缓存中移除。当SQL Server存在内存压力的时候,一些执行计划也会被移除,释放内存。
Hint提示
在大部分情况下,查询优化器都会选择比较高效的执行计划,
但是,在有些情况下,查询优化器选择的执行计划没有达到预期的效果,或者说,查询优化器做出了错误的选择。造成这个问题的原因是我们没有为SQL Server提供准确的信息,例如数据库的统计信息过期了。
同时,在有些情况下,我们根据我们的经验和分析判断,发现SQL Server选择的执行计划不是我们想要的。
当遇到上面的情况的时候,我们就可以给查询优化器一些提示信息,去告诉它该如何产生执行计划,也就是我们自动的去干预查询优化器的默认行为。
为了让大家有一个感性的认识,我这里举一个例子,对于下面的查询:
我们通过查询它的执行计划,如下图所示:
我们发现,这个查询计划不够高效,于是我们改写查询语句,如下:
在语句中,我们强制的使得查询优化器选择我们要的连接方式,
执行计划如下:
大家如果对这里的知识不明白,没有关系,这里的例子只是让大家感受一下。
注意:一般情况下,我们没有必要去干扰查询优化器的工作,因为它会已经足够的“智能”去选择更好的执行计划,除非我们非常有信心,并且证明我们用一些Hint会提升性能,这个时候,我们可以加入Hint。事实是,我们加入的Hint都是会产生很多的问题。
本篇就暂时到这里!后一篇文章就稍微详细一点的来看看与执行计划相关的一些话题。