您的位置:首页 > 数据库

SQL Server 查询优化(测试03)执行计划优化

2015-01-06 22:47 357 查看
【Adhoc 即席查询优化】

有些sql语句查询只有一次,查询后生成计划缓存占用较大的空间。
如果有较多的即席查询,将会占用更多的内存空间。

下面测试即席查询优化:(上篇操作中,是没有优化的情况)


use AdventureWorks2012
go

--查看当前实例即席查询优化配置是否开启
exec sp_configure 'optimize for ad hoc workloads'




--开启!(只会影响新计划,已在计划缓存中的计划不受影响)
exec sp_configure 'show advanced options',1
reconfigure
go
exec sp_configure 'optimize for ad hoc workloads',1
reconfigure with override
go

--下面测试查看
use AdventureWorks2012
go

--先清空计划缓存  
DBCC FREEPROCCACHE  

--执行1次
select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =870
  
--连续执行2次
select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =897

  
--  查看缓存查询计划和计划大小  
SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_plan  
FROM sys.dm_exec_cached_plans   
CROSS APPLY sys.dm_exec_sql_text(plan_handle)  
CROSS APPLY sys.dm_exec_query_plan(plan_handle)  
WHERE [text] LIKE '%SalesOrderID%' AND [text] NOT LIKE '%sys%'



首次编译批处理时在计划缓存中存储一个编译的小计划存根(Compiled Plan Stub),而不是存储完全编译的计划。 
这种情况下不会让未重复使用的编译计划填充计划缓存,从而有助于缓解内存压力(缓存大小为272字节)。
编译的计划存根使数据库引擎能够识别此临时批处理以前已经过编译,但只存储了编译计划存根,
因此当再次调用(编译或执行)此批处理时,数据库引擎会对此批处理进行编译,
从计划缓存中删除编译计划存根并将完全编译的计划添加到计划缓存中。

也就是当第一次执行时,数据库引擎不会记录太多,数据库引擎可以认为你不会再次执行了,不会存储计划而浪费空间。
当第二次执行时,系统开始“认真对待”,重新编译并完整缓存计划。

所以对于执行2次的sql批处理,缓存对象才创建真正的执行计划(Compiled Plan),并占有缓存139264字节!


【简单参数化】

用于提高关系引擎将复杂的 SQL 语句与现有的、未使用的执行计划相匹配的能力;
在内部对该语句进行参数化以增加将其与现有执行计划相匹配的可能性。此过程称为简单参数化。

--取消即席查询优化
exec sp_configure 'optimize for ad hoc workloads',0
reconfigure with override
go

--查看或设置简单参数



USE [master]
GO
ALTER DATABASE [AdventureWorks2012] SET PARAMETERIZATION SIMPLE WITH NO_WAIT
GO


--下面测试查看
use AdventureWorks2012
go

--清空计划缓存  
DBCC FREEPROCCACHE  

--分别执行以下查询
select * from [Production].[Product] where ProductID = 870
select * from [Production].[Product] where ProductID = 897

--查看缓存对象类型
SELECT cacheobjtype,objtype,refcounts,usecounts,[sql]  
FROM sys.syscacheobjects  
WHERE [sql] LIKE '%ProductID%' AND [sql] NOT LIKE '%sys%'



缓存计划中,有一个对象类型为Prepared(预定义语句),两个查询语句都使用了相同的模板,
并且系统预定义的模板参数类型为小整型((@0 smallint)),占用2个字节(16位),范围:-2^15(-32,768)到2^15-1(32,767)。
当我使用的条件不一样时,如ProductID = 1,数据库引擎将不会用刚才的缓存计划模板执行,
而是重新生成一个模板

--分别执行以下查询
select * from [Production].[Product] where ProductID = 1

--查看缓存对象类型
SELECT cacheobjtype,objtype,refcounts,usecounts,[sql]  
FROM sys.syscacheobjects  
WHERE [sql] LIKE '%ProductID%' AND [sql] NOT LIKE '%sys%'



新的模板参数类型为微整型((@0 smallint)),占用1个字节(8位),范围:0~255。
这样并没有重用原来的缓存模板。(解决办法是使用“强制参数化”,下面在讲)


--再测试一个
--清空计划缓存  
DBCC FREEPROCCACHE  

--这个不会自动简单参数化
select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =870

--使用sp_executesql 可以参数化
exec sp_executesql N'select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =@ProductID',N'@ProductID INT',870

exec sp_executesql N'select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =@ProductID',N'@ProductID INT',897

--  查看缓存查询计划和计划大小  
SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_plan  
FROM sys.dm_exec_cached_plans   
CROSS APPLY sys.dm_exec_sql_text(plan_handle)  
CROSS APPLY sys.dm_exec_query_plan(plan_handle)  
WHERE [text] LIKE '%ProductID%' AND [text] NOT LIKE '%sys%'



通过sp_executesql在内定义参数达到参数化,但是执行计划是以第一次执行的参数产生的计划为准!
首次执行决定了执行计划的永久模板。


【强制参数化】

可指定数据库中所有的 SELECT、INSERT、UPDATE 和 DELETE 语句参数化,作用和简单参数化一样。
但也有很多特殊结构的sql语句不能参数化,更多参考 强制参数化

USE [master]
GO
ALTER DATABASE [AdventureWorks2012] SET PARAMETERIZATION FORCED WITH NO_WAIT
GO

use AdventureWorks2012
go

--清空计划缓存  
DBCC FREEPROCCACHE  

--分别执行以下2个sql查询语句
select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =870

select sdh.SalesOrderID,sdh.SalesOrderNumber,P.ProductID,p.Name,sod.LineTotal  
from [Sales].[SalesOrderHeader] sdh  
inner join [Sales].[SalesOrderDetail] sod on sdh.SalesOrderID = sod.SalesOrderID  
inner join [Production].[Product] p on sod.ProductID = p.ProductID  
where P.ProductID =897

--  查看缓存查询计划和计划大小  
SELECT refcounts,usecounts,cacheobjtype,size_in_bytes,[text],query_plan  
FROM sys.dm_exec_cached_plans   
CROSS APPLY sys.dm_exec_sql_text(plan_handle)  
CROSS APPLY sys.dm_exec_query_plan(plan_handle)  
WHERE [text] LIKE '%ProductID%' AND [text] NOT LIKE '%sys%'



其实缓存与简单参数化一样。只是可以参数化的sql比简单参数化的更多。


-------------------------------------------------
(个人看法)
通过上面测试,较好的配置是:强制参数化,打开即席查询优化。

强制参数化 可生成一个结构相同的sql共同计划模板和每个sql的计划(占内存!)
即席查询优化 可让每个sql的计划(占内存!)不会生成,只存根。(只对内存而已,减少内存,CPU方面没参与测试)



简单参数化

强制参数化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: