当 Sql Server 收到任何一个指令,包括:查询、批处理、存储过程、触发器、预编译指令和动态SQL Server语句,要完成语法解析、语义分析,然后再进行"编译",生成能够运行的"执行计划"。在编译的过程中,SQL Server 会根据所涉及的对象的架构、统计信息,以及指令的具体内容,估算可能的执行计划,以及它们的成本,最后选择一个SQL Server认为成本最低的语句。
执行计划生成之后,SQL Server 通常会把它们缓存到内存里,术语统称它们叫“Plane Cache”。以后同样的语句执行,SQL Server就可以使用同样的执行计划,而无须再做一次编译。这种行为,叫做“重用”。但是有时候,哪怕是一模一样的语句,SQL Server 下次执行还是要再做一次编译。这种行为叫“重编译”。执行计划的编译和重编译都是要耗费资源的。
执行计划的好坏当然决定了语句的执行速度。对于同样一条语句,使用好的执行计划可能会比差的要快几百倍,甚至几千倍。所以从这一角度上来讲,没运行一条语句,都把它先编译一遍当然是最好的。它能够保证使用的执行计划是 SQL Server 能找到的最优的。但是 SQL Server 每秒钟可能会运行成百上千的指令。如果每个都编译一遍,是资源的严重浪费。所以 SQL Server 在这里也试图寻找一个平衡点,使用优先的 complie/recomplie,得到最好的 整体性能。
查看 SQL Server 缓存的执行计划,可以运行下面这条语句:Select*From Sys.syscacheobjects
对不同的指令调用方法, SQL Server 做执行计划缓存和重用机制也有所不同。下面介绍最常见的几种:
1. Adhoc 语句
一组包含 Select、Insert、Update、Delete 的批处理指令。对这样的指令,只有前后完全一直,包括字母的大小写、空格、回车换行都一致, SQL Server 才认为是两条一样的语句,才能够重用执行计划。所以这个要求还是挺高的。
2. 用 Exec() 的方式运行动态 SQL Server 语句
有些应用程序为了开发上的灵活程度,在程序运行过程中,动态地拼接成一个语句字符串,然后用 Exec() 的方式执行。这种调用方法被称为“dynamic SQL”。它的好处就是很灵活,可以根据客户的选择,动态生成指令,而不仅限于预定义的那几种。但是它的缺点也是太灵活了,客户发过来的语句每次都不一样,或者语句主体部分是一样的,但是参数不一样, SQL Server 都要做编译。这点和 adhoc 语句是一样的。
3. 自动参数化查询
对于一些比较简单的查询, SQL Server 2005 自己就可以做自动参数化,把语句里的参数用一个变量代替,但是这仅限于很简单的查询。
4. 用 sp_executesql 的方式调用的指令
查询自动参数化在很多种条件下是不支持的,而且它还是要为每句查询生成一个 adhoc 的执行计划。所以它并不是减少比哪一的最有手段。改用 sp_executesql 能够更有效地增加执行计划重用。
5. 存储过程
对用户经常要调用的指令,把他们做成存储过程,既方便管理、规范脚本,又能够大大提高执行计划调用,是值得推荐的一种做法。从 SQL Server 的角度,最好绝大多数指令都能够以存储过程的方式调用,尽量少使用 Dynamic SQL 的方式。
但是有些时候, SQL Server 为了确保返回正确的值,或者有性能上的顾虑,有意不重用缓存在内存里的执行计划,而现场编译一份。这种行为,被成为重编译。下面是比较常见的会发生重编译的情形:
1. 当指令或批处理所涉及的任何一个对象(表格或者试图)发生了架构(schema)变化
例如,在表或者视图上添加或删除另一个字段、添加或删除了一个索引,在表上添加或者删除了一个约束条件等。定义发生了变化,原来的执行计划就不一定正确了,当然要重编译。
2. 运行过 sp_recomplie 后
当用户在某个存储过程或者触发器上运行过 sp_recomplie 后,下一次运行它们就会发生一次重编译。如果用户在某个表或试图上运行了 sp_recomplie ,那么所有引用到这张表或视图的存储过程在下一次运行前,都要做重编译
3. 有些动作会清除内存里的所有执行计划,迫使大家都要做重编译
Detach 一个
对数据库做了一个升级,在新的服务器上会发生执行计划清空
运行了 DBCC FreeProccache 语句
运行了 ReConfigure 语句
运行了 Alter DataBase ... Modify FileGroup 语句
用 Alter DataBase ... Collate 语句修改了某个数据库的字符集
下列动作会清楚 SQL Server 服务器缓存的某个数据库的执行计划:
DBCC FlushProcinDB 语句
Alter DataBase ... Modify Name 语句
Alter DataBase ... Set Online 语句
Alter DataBase ... Set Offline 语句
Alter DataBase ... Set EmerGency 语句
Drop DataBase 语句
当一个数据库自动关闭时
DBCC CheckDB 语句结束时
4. 当一些 Set 开关值变化后,先前的那些执行计划都不能重用
5. 当表格或试图上的统计信息发生变化后
当统计信息被手动跟新后,或者 SQL Server 发现某个统计信息需要自动更新时, SQL Server 会对所涉及的语句都做重编译。
须说明的是,在 Sql Server 里,执行计划重用并不一定是一件好事,而编译重编译也不一定是一件坏事。在 Sql Server 里,能对计划重用和编译/重编译产生影响的功能主要有:
1. 使用存储过程,或者 sp_executesql 的方式调用会被重复使用的语句,而不要直接用 ad-hoc 语句或者 dynamic SQL 。
2. 在语句里引用对象(表、视图、存储过程等),到带上它的 schema 名字,而不光是对象自己的名字。
3. 将 数据库 Parameterization 属性设置成 Forced
这个属性是开启数据库强制参数化。也就是说,对于在这个数据库下运行的大部分语句,SQL Server 都会先参数化,再运行。如果应用经常用 adhoc 的方式调用一样的语句,强制参数化可能会有所帮助
4. 统计信息更新
统计信息手工或者自动更新后,对和它有关的执行计划都不再能重用,而会产生重编译。
5. Create Procedure ... with Recompile 选项 和 Exce ... with Recomplie 选项
在重建或者调用存储过程的时候使用 "with Recomplie",会强制 Sql Server 在调用这个存储过程的时候,永远都要先编译,再运行。
6. 用户使用了 sp_recomplie
7. 用户在调用语句的时候,使用了 "Keep Plan" 或者 "KeepFixed Plan" 这样的查询提示