探秘重编译(Recompilations)(2/2)
2015-06-15 11:50
330 查看
原文:探秘重编译(Recompilations)(2/2)在上一篇文章里,我讨论了使用临时表如何引起SQL Server里的重编译。在文章最后我提到,今天这篇文章我会聚焦[b]表变量(Table Variables)[/b]的更多信息,它可以避免重编译的昂贵开销。我们来详细分析下。
表变量的好处是它们不会引起任何重编译。当你执行这个存储过程并用SQL Server Profiler跟踪时,不会发现重编译事件。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/9c68284cf9431083b11bd77404e49c86.png)
为什么使用表变量就可以这样呢?首先表变量就是个变量——名副其实。当你定义你的表变量时,意味着你不会改变你的数据库架构。因此基于数据酷架构改变的重编译就可以避免。另外表变量是没有统计信息的。因此没有统计信息需要维护,第2个引起重编译原因也就消失了。
首先,这2样听起来都很棒,但当我们进一步分析时,就会发现它的重大缺点。我们来看看。表变量近乎就是个变量。在临时表里,表变量还是持续的。是的,你没看错:当你使用表变量时,会涉及到临时表里的物理I/O操作。这个可以用动态管理视图[b]sys.dm_db_session_space_usage[/b]来验证,它是在会话级别跟踪临时表的使用率。我们来看下面的代码(请【新建查询】执行下列代码):
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/85ed0a1f7068b926e7201bb152080073.png)
从图中可以看出,这个表变量在临时表里需要分配5个页。因为这个表变量已经超过范围,这5个页面也已被标记为重分配(deallocation)。你要知道这个副作用。
表变量也没有统计信息。因此这里没有重编译发生。但是作为一个副作用,查询优化器始终认为估计行数为1.这个会非常,非常糟糕。如果你从表变量连接你数据库里另外一张表。在那个情况下,查选优化器在执行计划里引入嵌套循环连接(Nested Loop Join)运算符,引用的表变量作为外表,因为估计行数是1。如果事实上返回行是10000或更多的话,整个执行计划就谈不上最优。我们来看下面的例子(点击工具栏的
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/aff6e9d7f177a2170ea5d80d0cff1f71.png)
显示包含实际的执行计划):
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/4e5a722dd6a24c0bc50821d0b633b26d.png)
我们仔细看下[b]聚集索引扫描( Clustered Index Scan)[/b]运算符的属性信息,你会看到这里的估计行数是1,而实际行数却是12622。
你可以通过自SQL Server 2005起引入的[b]语句级别的重编译(Statement-Level Recompilation)[/b]来修正这个参数预估错误。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/a67aa9f80e4bb53fbfdf4f2e41be6d94.png)
但是这个方法有点产生相反效果的(counter-productive),因为你又引入了重编译,原先你使用表变量就是为了避免重编译。
作为通常的经验法则(general rule-of-thumb),对于大数量的数据,你应该使用临时表,表变量用在小数量的数据上。但是你真的要为你的工作量测试(benchmark)下,来决定什么时候使用临时表,什么时候使用表变量是正确的。
表变量(Table Variables)
表变量总局限于提交到SQL Server的批处理语句范围。当你在批处理语句范围外引用表变量时,SQL Server就会返回你一条错误信息。这是和临时表相比第1个重大区别。下列代码向你展示了如何创建和使用表变量——只在简单存储过程的上下文里。CREATE PROCEDURE DemonstrateTableVariablesNoRecompiles AS BEGIN DECLARE @tempTable TABLE ( ID INT IDENTITY(1, 1) PRIMARY KEY, FirstName CHAR(4000), LastName CHAR(4000) ) INSERT INTO @TempTable (FirstName, LastName) SELECT TOP 1000 name, name FROM master.dbo.syscolumns SELECT * FROM @TempTable END GO
表变量的好处是它们不会引起任何重编译。当你执行这个存储过程并用SQL Server Profiler跟踪时,不会发现重编译事件。
EXEC dbo.DemonstrateTableVariablesNoRecompiles
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/9c68284cf9431083b11bd77404e49c86.png)
为什么使用表变量就可以这样呢?首先表变量就是个变量——名副其实。当你定义你的表变量时,意味着你不会改变你的数据库架构。因此基于数据酷架构改变的重编译就可以避免。另外表变量是没有统计信息的。因此没有统计信息需要维护,第2个引起重编译原因也就消失了。
首先,这2样听起来都很棒,但当我们进一步分析时,就会发现它的重大缺点。我们来看看。表变量近乎就是个变量。在临时表里,表变量还是持续的。是的,你没看错:当你使用表变量时,会涉及到临时表里的物理I/O操作。这个可以用动态管理视图[b]sys.dm_db_session_space_usage[/b]来验证,它是在会话级别跟踪临时表的使用率。我们来看下面的代码(请【新建查询】执行下列代码):
-- Create a table variable DECLARE @tempTable TABLE ( ID INT IDENTITY(1, 1) PRIMARY KEY, FirstName CHAR(4000), LastName CHAR(4000) ) -- Insert 4 records into the table variable INSERT INTO @tempTable (FirstName, LastName) VALUES ( 'Woody', 'Tu' ), ( 'Woody', 'Tu' ), ( 'Woody', 'Tu' ), ( 'Woody', 'Tu' ) -- Retrieve the data from the table variable. -- The execution plan estimates 1 row. SELECT * FROM @tempTable GO -- Review the space used in TempDb. -- Our table variable currently needs 5 pages in TempDb. -- The 5 needed pages from the table variable are already marked for deallocation (column "user_objects_dealloc_page_count") SELECT * FROM sys.dm_db_session_space_usage WHERE session_id = @@SPID GO
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/85ed0a1f7068b926e7201bb152080073.png)
从图中可以看出,这个表变量在临时表里需要分配5个页。因为这个表变量已经超过范围,这5个页面也已被标记为重分配(deallocation)。你要知道这个副作用。
表变量也没有统计信息。因此这里没有重编译发生。但是作为一个副作用,查询优化器始终认为估计行数为1.这个会非常,非常糟糕。如果你从表变量连接你数据库里另外一张表。在那个情况下,查选优化器在执行计划里引入嵌套循环连接(Nested Loop Join)运算符,引用的表变量作为外表,因为估计行数是1。如果事实上返回行是10000或更多的话,整个执行计划就谈不上最优。我们来看下面的例子(点击工具栏的
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/aff6e9d7f177a2170ea5d80d0cff1f71.png)
显示包含实际的执行计划):
CREATE PROCEDURE BadPerformingQuery AS BEGIN DECLARE @tempTable TABLE ( ID INT IDENTITY(1, 1) PRIMARY KEY, FirstName CHAR(4000), LastName CHAR(4000) ) INSERT INTO @TempTable (FirstName, LastName) SELECT TOP 20000 name, name FROM master.dbo.syscolumns -- The physical Join Operator will be a Nested Loop, -- because Nested Loop is optimized for 1 row in the outer loop. SELECT * FROM AdventureWorks2008R2.Person.Person p INNER JOIN @tempTable t ON t.ID = p.BusinessEntityID END GO
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/4e5a722dd6a24c0bc50821d0b633b26d.png)
我们仔细看下[b]聚集索引扫描( Clustered Index Scan)[/b]运算符的属性信息,你会看到这里的估计行数是1,而实际行数却是12622。
你可以通过自SQL Server 2005起引入的[b]语句级别的重编译(Statement-Level Recompilation)[/b]来修正这个参数预估错误。
-- Use a statement-level recompilation to fix the problem with the -- cardinality estimation. ALTER PROCEDURE BadPerformingQuery AS BEGIN DECLARE @tempTable TABLE ( ID INT IDENTITY(1, 1) PRIMARY KEY, FirstName CHAR(4000), LastName CHAR(4000) ) INSERT INTO @TempTable (FirstName, LastName) SELECT TOP 20000 name, name FROM master.dbo.syscolumns -- The physical Join Operator will be a Nested Loop, -- because Nested Loop is optimized for 1 row in the outer loop. SELECT * FROM AdventureWorks2008R2.Person.Person p INNER JOIN @tempTable t ON t.ID = p.BusinessEntityID OPTION (RECOMPILE) END GO
![](https://oscdn.geek-share.com/Uploads/Images/Content/202004/01/a67aa9f80e4bb53fbfdf4f2e41be6d94.png)
但是这个方法有点产生相反效果的(counter-productive),因为你又引入了重编译,原先你使用表变量就是为了避免重编译。
小结
使用表变量你可以避免SQL Server里重编译的负荷,但同样也有副作用。最大的副作用就是错误参数估计——估计行数为1。因此当你和小数量行打交道时可以使用表变量,因为那时错误的参数预估并不重要,也不影响你的性能。但和大量数据行打交道时,它会伤害你的性能,因为生成了低效的执行计划。作为通常的经验法则(general rule-of-thumb),对于大数量的数据,你应该使用临时表,表变量用在小数量的数据上。但是你真的要为你的工作量测试(benchmark)下,来决定什么时候使用临时表,什么时候使用表变量是正确的。
相关文章推荐
- Objective-C Method Swizzling 的最佳实践
- 简介JavaScript中用于处理正切的Math.tan()方法
- Servlet--SingleThreadModel接口,RequestDispatcher接口
- ios判断App是否安装
- 管理系统UI之一:淡化System Bar(Dimming the System Bars)
- 脑波和脑图
- 正则表达式的使用
- operator.cpp
- Servlet--SingleThreadModel接口,RequestDispatcher接口
- SQL Server如何在变长列上存储索引
- Linux运维工程师入门须掌握的10个技术点
- 【插件】特效发射粒子数查找工具
- 第十章:Replica Sets + Sharding
- mac+windows 双系统
- Extjs2.2 获取PagingToolbar的当前页和总页数
- 开发一个Servlet示例
- 猫扑论坛的沦落 新社交的崛起
- 《实体解析与信息质量》- 2.1.8 信息质量(IQ)作为一门学科
- 脑波设备mindwaveTGC接口示例
- iostream.cpp