浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用
我们知道,SqlServer执行sql语句的时候,有一步是对sql进行编译以生成执行计划,
在生成执行计划之前会去缓存中查找执行计划
如果执行计划缓存中有对应的执行计划缓存,那么SqlServer就会重用这个执行计划缓存,避免编译,从而提高效率,
对于开发者来说,为了达到能够重用执行计划的目的,使用参数化的sql是一个必要的条件。
除了参数化的sql,对于即席查询或者是动态生成的查询语句,也就是非参数化的sql语句,SqlServer本身也在对一些sql进行自动优化处理。
在SqlServer层面,分为简单参数化和强制参数化两种方式,SqlServer数据库中对sql的解析方式,默认是简单参数化,当然也可以设置为强制参数化。
在简单参数化模式下,SqlServer对查询有一些专门的优化,就是sql查询条件一些变量的值,不会影响到查询的执行计划,
那么SqlServer会自动地进行参数化处理,后续如果有类似的查询,可以使用自动参数化的sql生成的执行计划,避免重编译,从而提高sql的执行效率以及减少服务器资源的消耗。
参数化的设置是一个数据库级别的选项,如下是通过图形界面看到的参数化默认设置方式,默认是“简单”模式,也可以设置为“强制”模式,

也可以通过脚本的方式来设置
--查看数据库的参数化方式 select name,is_parameterization_forced from sys.databases where name='dbtest' --默认是简单模式 alter database dbtest set parameterization simple --可以设置为强制模式 alter database dbtest set parameterization forced
首选来看简单参数化模式下的处理方式
这种情况下,如果一个查询的常量值不影响执行计划的方式,那么SqlServer会对这个查询生成一个参数化的sql,随后的查询中的即便常量的值发生了变化,也会重用上面进行参数化后生成的执行计划,
create table t_1
(
id int,
name varchar(50)
)
insert into t_1 values (1,'A')
insert into t_1 values (2,'B')
insert into t_1 values (3,'C')
insert into t_1 values (4,'D')
insert into t_1 values (5,'E')
--测试之前注意清空一下执行计划缓存
dbcc freeproccache
首选执行第一个查询
select * from t_1 where id=1
执行完成后,我们查询执行计划中的信息
select usecounts,size_in_bytes,objtype,t.text from sys.dm_exec_cached_plans p cross apply sys.dm_exec_sql_text(p.plan_handle) t where p.cacheobjtype='Compiled Plan' and t.text like '%t_1%' and t.text not like '%dm_exec_cached_plans%'

此时会观察到,
select * from t1 where id=1这一句sql本身也被缓存了,同时自动生成了一个objtype为prepared的执行计划,
此时再执行另外一个sql:select * from t1 where id=2

注意观察参数objtype为prepared的执行计划的usecounts,由第一次的1变为了2
说明第二句执行的sql重用了第一句sql自动参数化生成的执行计划,第一次自动参数化生成的执行计划得到了重用。
或许你会有一点疑问,第二个执行的sql也就是select * from t_1 where id=2
也生成了执行计划,为什么说这一句sql执行的时候重用了第一句sql生成的执行计划的缓存的呢?
这里会看到,执行的原始的sql语句生成的执行计划的类型都是Adhoc的,这种执行计划称之为壳查询(Shell Query),
(这里解释一下,Adhoc查询很多人称之为即席查询,但是这里类型是Adhoc查询,却不是即席查询,
即席查询时包含完整的执行计划的,这里的壳查询时不包含执行计划的,只能说所谓的即席查询是Adhoc类型的查询的一种
上面查询执行计划的sql再加一个DMV,如下,就会发现,生成的Adho执行计划缓存中q.query_plan为空,并不包含完整的执行计划
select usecounts,size_in_bytes,objtype,t.text ,q.query_plan
from sys.dm_exec_cached_plans p
cross apply sys.dm_exec_sql_text(p.plan_handle) t
cross apply sys.dm_exec_query_plan(p.plan_handle) q
where p.cacheobjtype='Compiled Plan'
and t.text like '%t_1%'
and t.text not like '%dm_exec_cached_plans%')
这个壳查询也会缓存,但是其未包含完整的执行计划,只包含sql的文本以及执行真正执行计划的指针,
缓存壳查询的目的在于当执行与壳查询完全相同的sql时,查询语句可以快速地找到其对应的真正的执行计划
缓存壳查询的目的在于:
用户发送给SqlServer的是一个文本,因为可能每次查询条件不同,文本本身也是不同的,SqlServer缓存的是一个参数化的执行计划,如果用户发送过来的文本匹配上了壳查询,壳查询又存储了一个指向真正执行计划的指针,那么就可以将用户发送过来的sql快速连接到其执行计划,是作为连接用户发送过来的sql和自动参数化生成的执行计划的一道桥梁。另外,缓存壳查询因为没有包含完整的执行计划,所以其占用的内存也并没有一个完整的执行计划占用的内存大,
从size_in_bytes字段也可以看到,壳查询的缓存的空间是没有参数化的sql的占用空间大的。
在执行select * from t_1 where id=1之后,生成了参数化的执行计划,
随后如果多次执行select * from t_1 where id=2,会发现参数化后的执行计划usecounts并没有增加,一直为2,而壳查询的计划的usecounts在增加,
上面说了,“缓存壳查询的目的在于当执行与壳查询完全相同的sql时,查询语句可以快速地找到其对应的真正的执行计划”,
此时虽然真正使用的是参数化执行计划,但是DMV中只是将壳查询的usecounts计数器加1
那么回头再想一个问题,对于简单参数模式下,SqlServer什么情况下回对sql语句自动参数化?
简单模式下,SqlServer并不是所有的sql都进行参数化处理,只有对那些有且只有一种执行计划的sql语句,才自动参数化处理,
比如上面的查询,因为t_1表上的id列上没有任何索引,不管对于任何一个值,都只能是全表扫描的方式去执行这个查询,此时SqlServer会做到自动参数化,生成一个可重用的执行计划。
如果id列上有一个非唯一的索引,那么select * from t_1 where id=***这个查询就有可能存在不同的执行计划,
比如是索引查询或者是全表扫描,这个要依据列上的统计信息了(这个话题也比较大,就不展开说了),
也就是说,在不同的查询条件下,有可能生成不同的执行计划,那么SqlServer就不会为其生成参数化的sql。
比如在id列上见一个非唯一的索引,那么再次观察执行计划缓存,就发现SqlServer并没有生成一个自动参数化的执行计划。
--测试之前注意清空一下执行计划缓存 dbcc freeproccache --在id列上创建一个非唯一的索引 create index idx_id on t_1(id) --此时执行这个查询 select * from t_1 where id=1 --查看执行计划的缓存 select usecounts,size_in_bytes,objtype,t.text from sys.dm_exec_cached_plans p cross apply sys.dm_exec_sql_text(p.plan_handle) t where p.cacheobjtype='Compiled Plan' and t.text like '%t_1%' and t.text not like '%dm_exec_cached_plans%'

此时会发现,SqlServer并没有自动地生成一个参数化的sql,就是因为对于select * from t_1 where id=1这个查询
可能索引查找是最优化的,但是如果把id的值换做另外其他的未知的值,可能表扫描是最后化的,也就是说,在这种情况下,当前查询的执行计划不具备通用性
所以此时SqlServer并不会生成自动参数化的sql执行计划。
总结:SqlServer在执行sql的的生成执行计划的时候,除了人为因素(比如参数化的sql)的影响,SqlServer本身也会对一些没有参数化的sql做自动参数化,以最优化的方式去运行sql指令,提高执行效率的目的。
浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用的更多相关文章
- SQL Server SQL性能优化之--数据库在“简单”参数化模式下,自动参数化SQL带来的问题
数据库参数化的模式 数据库的参数化有两种方式,简单(simple)和强制(forced),默认的参数化默认是“简单”,简单模式下,如果每次发过来的SQL,除非完全一样,否则就重编译它(特殊情况会自动参 ...
- SqlServer 中如何查看某一个Sql语句是复用了执行计划,还是重新生成了执行计划
我们知道SqlServer的查询优化器会将所执行的Sql语句的执行计划作缓存,如果后续查询可以复用缓存中的执行计划,那么SqlServer就会为后续查询复用执行计划而不是重新生成一个新的执行计划,因为 ...
- 浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色
简介 在简单恢复模式下,日志文件的作用仅仅是保证了SQL Server事务的ACID属性.并不承担具体的恢复数据的角色.正如”简单”这个词的字面意思一样,数据的备份和恢复仅仅是依赖于手动备份和恢复.在 ...
- sql server 备份与恢复系列三 简单恢复模式下的备份与还原
一.概述 前面讲了备份的一些理论知识,这篇开始讲在简单恢复模式下的备份与还原.在简单模式下是不能做日志备份的,发生灾难后,数据库最后一次备份之后做的数据修改将是全部丢失的,所以在生产环境下,数据又很重 ...
- SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案
parameter sniff问题是重用其他参数生成的执行计划,导致当前参数采用该执行计划非最优化的现象.想必熟悉数据的同学都应该知道,产生parameter sniff最典型的问题就是使用了参数化的 ...
- 获取SQLServer的最完整数据字典的SQL语句
原文:获取SQLServer的最完整数据字典的SQL语句 原创于2008年06月18日,2009年10月18日迁移至此. 获取SQLServer 的最完整数据字典的SQL 语句 其实网上已经流传了 ...
- SQL Sever 2008性能分析之执行计划
一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试 2的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<Inside Microsoft SQL ...
- SQL点滴27—性能分析之执行计划
原文:SQL点滴27-性能分析之执行计划 一直想找一些关于SQL语句性能调试的权威参考,但是有参考未必就能够做好调试的工作.我深信实践中得到的经验是最珍贵的,书本知识只是一个引导.本篇来源于<I ...
- 一条SQL语句在MySQL中如何执行的
本篇文章会分析一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的. 在分析之前我会先带着你看看 MySQL 的基础架构, ...
随机推荐
- 一个简单的python线程池框架
初学python,实现了一个简单的线程池框架,线程池中除Wokers(工作线程)外,还单独创建了一个日志线程,用于日志的输出.线程间采用Queue方式进行通信. 代码如下:(不足之处,还请高手指正) ...
- 超链接标签a样式生效,取消下划线,文字垂直(上下)居中
直接设置超链接标签a的属性时并不会生效, 需要将display属性改为inline-block, 即style="display:inline-block" 添加标签a时,默认是有 ...
- 我的ORM之十-- MyOql实现原理
我的ORM索引 ORM的功能 两个基本功能 翻译:对象查询转化为SQL 映射:把查询结果转化为实体 更多功能 如果仅仅实现这两个功能,那这两个是可以独立的,实现起来也就很简单了.但MyOql的目标还有 ...
- DBCP连接池使用问题
问题现象: 启动应用,访问无压力,一切正常,一段时间过后,应用访问异常. 问题分析: 1.web容器线程爆满,拒绝服务.由于应用并发量大,线程响应时间长增加,线程池连接数逐步递增直到爆满,导致应用拒绝 ...
- 移动App开发需要更多的PaaS平台而不是IaaS
时代的变迁,创业的大潮,越来越多的人关注了有点开发,越来越多的人了解了互联网服务术语:PaaS.IaaS.SaaS.BaaS等.今天大家在开发App的时候这么多复杂的云服务如何来选择呢? IaaS服务 ...
- http学习笔记(二)—— 嘿!伙计,你在哪?(URL)
我们之所以希望浏览网页,其中一个重要的原因就是庞大的web世界中有很丰富的资源,他就像哆啦a梦的口袋,随时都能拿出我们想要的宝贝.这些资源通过http被传送到我们的浏览器,并展示到我们的屏幕上.而我们 ...
- 从Windows中卸载Apache
在重装Apache或者妳不再需要它的时候,这时就需要将它卸载. 下面是步骤: 打开开始菜单(win8中ÿ+X)或者我的电脑(废话) 找到并打开Apache的安装目录(Program Files\Apa ...
- 细说.NET 中的多线程 (一 概念)
为什么使用多线程 使用户界面能够随时相应用户输入 当某个应用程序在进行大量运算时候,为了保证应用程序能够随时相应客户的输入,这个时候我们往往需要让大量运算和相应用户输入这两个行为在不同的线程中进行. ...
- .NET学习笔记 -- 那堆名词到底是啥(CLR、CLI、CTS、CLS、IL、JIT)
什么是CLR? CLR,公共语言运行时(Common Language Runtime)是一个由多种语言使用的“运行时”.他的核心功能包括(内存管理.程序集加载.安全性.异常处理和线程同步),可以被面 ...
- [_CN] Eclipse精要与高级开发技术 note
一 eclipse是基于java的 ide ,但根据其体系结构,开发插件,也可拓展到其他语言———————— 尽管如此,但还是很少听说用eclipse来写php或者c的 跨os 三个项目:eclips ...