今天我想谈下性能调优培训里的重编译(Recompilations )。当你执行一个查询,SQL Server里另一个变动使你执行计划的剩余部分无效,就会发生重编译。在那个情况下SQL Server需要保证你执行计划的准确性,重编译就会被触发。重编译会给你的SQL Server带来额外的CPU开销。
什么是重编译?
首先我想展示下编译和重编译之间的区别。2个星期前,我们讨论了SQL Server里的编译。当查询优化器把提交的查询转化为实际执行计划时,编译就会发生。这就是说编译在查询执行开始前就发生。
另一方面,重编译在查询执行期间就会发生。因此SQL为了保证执行计划的准确性就重编译执行计划的剩余部分。如果执行计划里引用的索引在计划执行时被删除了。这就导致不可接受的结果。SQL Server触发重编译有2个类型:
- 基于正确性的重编译(Correctness-based Recompilations)
- 基于最优性能的重编译(Optimality-based Recompilations)
我们来详细看下这2类重编译。当计划不再准确,就会发生基于正确性的重编译(Correctness-based Recompilations)。例如你的数据库架构发生改变(新增或删除索引,删除统计信息),或者你的SET选项发生改变。在那个情况下,重编译就是为了保证你计划的准确。
如果你的统计信息发生改变,就会发生基于最优性能的重编译(Optimality-based Recompilations)。统计信息发生改变,一方面是SQL Server会自动更新你的统计信息,另一方面是你触发了统计信息的人为更新。那样的情况可以是书签查找正越过临界点,SQL Server需要引入全表/聚集索引扫描。
我们现在再来详细看一个在查询执行期间,触发很多重编译的常见特殊情景——临时表(Temp Tables)!
临时表(Temp Tables)
是的,你没看错:当你与临时表(Temp Tables)打交道时,在SQL Server里你会引起重编译。我们来一个非常简单的存储过程定义:
1 CREATE PROCEDURE DoWork 2 AS 3 BEGIN 4 CREATE TABLE #TempTable 5 ( 6 ID INT IDENTITY(1, 1) PRIMARY KEY, 7 FirstName CHAR(4000), 8 LastName CHAR(4000) 9 ) 10 INSERT INTO #TempTable (FirstName, LastName) 11 SELECT TOP 1000 name, name FROM master.dbo.syscolumns 12 SELECT * FROM #TempTable 13 END 14 15 GO
这个存储过程创建了一个简单的临时表,往它里面插入了几条记录,最后从表里获取几条记录。很简单,是不是?关键是这个存储过程在执行期间触发了2个重编译:
- 第1个触发重编译是因为你创建了一个新的临时表。通过创建临时表你就在改变你的数据库架构。这个触发了基于正确性的重编译(Correctness-based Recompilations)。
- 当你执行SELECT语句时,你触发了第2个重编译。刚才你在临时表里插入了几条记录,因此SQL Server需要更新你的统计信息。这里你就引入了基于最优性能的重编译(Optimality-based Recompilations)。
如何避免这2个重编译呢?你可以使用表变量(Table Variables)代替临时表。用表变量的话,你就不再改变数据库架构了(它只是个变量),而且表变量是没有统计信息的。这2个重编译就消失了。但是当然,用表变量会引入另一个性能问题:因为它们没有统计信息,SQL Server总是估计它们只有1行,因此你的参数预估就会完全一塌糊涂。
因此表变量在SQL Server里只有特殊使用情景:当你只和小量数据打交道时。当你和大量数据打交道时,你仍应该使用临时表,因为它们会给你准确的统计信息,你也可以在上面建立索引。缺点就是它们会触发重编译。
小结
今天我们讨论了性能调优培训里的重编译(Recompilations)。如你所见,因为SQL Server需要保证你的执行计划的准确性才会有重编译发生。我们还看了重编译经常发生的特殊场景——临时表(Temp Tables)。
这些重编译可以通过使用表变量来解决,但这里你也要意识到带来的副作用。下星期我会谈下SQL Server里的并行执行计划(Parallel Execution Plans),里面会有很多有趣的事情发生。好好享受接下来的7天,到时候见!