《Microsoft Sql server 2008 Internals》读书笔记--第九章Plan Caching and Recompilation(8)
《Microsoft Sql server 2008 Internals》读书笔记订阅地址:
http://www.cnblogs.com/downmoon/category/230397.html/rss
《Microsoft Sql server 2008 Internals》索引目录:
《Microsoft Sql server 2008 Internal》读书笔记--目录索引
上文主要介绍了已编译计划、执行上下文和计划缓存元数据相关的几个常用的系统函数,并介绍了几个葵花宝典级的调优语句。本文将继续关注缓存大小管理、缓存项的成本(Costing of Cache entries)
■缓存大小管理
我们已经了解计划重用和SQL Server如何在缓存中查找一个计划。现在,我们看看SQL Server如何管理计划缓存的大小以及它如何决定在缓存中没有空间时某个计划被移除。前面已经介绍的部分,全局操作类如DBCC FREEPROCCACHE会从缓存中清除所有计划,而alter procrdure时会从缓存中清除所有与这个存储过程相关的计划。此外,在大多数其他情况下,仅仅当SQL Server面临内存压力时才会从缓存中移除计划。SQL Server用于决定何时和计划如何应该被从缓存中移除的算法称为“回收策略”(eviction policy)。每个缓存过程都有自己的eviction policy,我们仅仅讨论对象计划过程和SQL计划过程。
决定哪个计划被回收是基于计划的成本,后文讨论。回收在SQL Server侦探到内存压力时开始,首先是零成本的计划被移除,其他计划的成本减半。两种内存压力为:本地内存压力和全局内存压力。
讨论内存压力时,我们不得不提到一个词,可见内存(visible memory),可见内存是在SQL Server缓冲池中可以直接地址化的可用物理内存。
在一个32位SQL Server实例中,可见内存的最大值为2-3GB,这取决于你在boot.ini文件中是否设置/3GB标志开关。大于这个数字的带有地址的内存仅仅通过AWE-mapped-memory间接实现。
而一个64位SQL Server实例中,所有内存全部可以直接地址化,全部是可见内存。 你可以通过一个名为sys.dm_os_sys_info的DMV的一个列bpool_visible来查看这个值,这是一个8KB的buffer值。
另外,请注意,SQL 2005的不同版本及不同的SP对应的计划缓存压力限定值均不一样,即SP!与SP2对应的值不一样。SQL Server2008也是如此。 32位与64位更是不同。
SQL Server版本 | Cache Pressure Limit |
SQL Server 2005 RTM&SP1 | 75%的可见目标内存0-8GB +50%的可见目标内存8GB-64GB +25%的可见目标内存>64GB |
SQL Server 2005 SP2&SP3 SQL Server 2008 RTM |
75%的可见目标内存0-4GB +10%的可见目标内存4GB-64GB +5%的可见目标内存>64GB |
SQL Server 2000 | 4GB upper cap on the plan cache |
例如,一个64位SQL Server 2008 RTM实例,28GB目标内存。那么这个上限(Limit)将是75%*4GB+10%*(28-4)GB=3+2.4=5.4GB
◆局部内存变量
如果单个缓存存储增长太大,它标示局部内存压力,SQL Server开始仅仅从该存储中移除项,这种行为防止一个存储占用太多的总系统内存。
在单个页分配时,如果缓存达到计划缓存压力上限的75%,如上表所示,或多个分配页时达到计划缓存压力限定的50%,内部内存压力被触发,计划开始从缓存中移除。如上例中,如果缓存存储达到75%*5.4GB=4.05GB,此时某些计划开始按指定成本顺序移除,如果刚好有一些计划添加到缓存,这一进一出,将会引起新计划的响应时间增加。
除了内存数量达到压力上限外,SQL Server在一个存储中计划数量达到该存储中哈希表大小的四倍时,也会触发内存压力上限。如上文所示。哈希表中大约10000-40000wh Bucket,也就意味着SQL存储或对象存储超过40000-160000项。下面的查询分别返回哈希表中的buckets数量和每个存储中荐的数量:
FROM sys.dm_os_memory_cache_hash_tables
WHERE type IN ('CACHESTORE_OBJCP', 'CACHESTORE_SQLCP');
GO
SELECT type, count(*) total_entries
FROM sys.dm_os_memory_cache_entries
WHERE type IN ('CACHESTORE_SQLCP', 'CACHESTORE_OBJCP')
GROUP BY type;
GO
在SQL Server 2008之前的版本,内部内存压力上限很少触发。因为在哈希表中的项数量总是被缓存存储中的计划的大小初始化。然而,在SQL Server 2008中,如果你启用Ad Hoc workloads优化,SQL Server 缓存存储中的实际项可能非常小(每一个已编译计划存根大约是300字节),因此,项的数量可能急剧上升而达到上限,远远大于存储的Size增长速度。如果没有启用Ad Hoc workloads优化,项的Size会非常大,因为每个计划的最小限制为24KB。下列查询返回缓存存储中的所有计划的大小:
SUM(size_in_bytes)/(1024.0 * 1024.0 * 1024.0)
AS size_in_gb_single_use_plans
FROM sys.dm_exec_cached_plans
GROUP BY objtype;
◆全局内存压力(Global Memory Pressure)
全局内存压力应用于缓存存储中的所有内存,可能是内部的或外部的。外部全部压力发生在操作系统侦探到SQL Server需要减少内存消耗经满足其他应用程序的内存需要时,总的缓存存储的size减少。
内部全局内存压力在虚拟地址空间很低时发生。也可能在memory broker预测已经使用的缓存存储超过压力限定值的80%。当发生时,所有缓存存储中将有项移除。
正如前面提到的,SQL Server侦探到内存压力时,所有零成本的计划先从缓存中移除,其他所有计划的总成本减半。任何特定的周期更新最多每个缓存存储的16项。不使用的从属对象比正在使用中的编译计划优先移除。从属对象包括可执行计划和游。在遇到内存压力时,为这些对象使用的内存的一半被移除。记住,从属对象(dependent Objects)的re-create是低成本的,特别是与已编译计划相比。
■缓存项的成本
决定什么计划比缓存中剔除基于它们自身的成本。对于Adhoc计划,成本被看作0,但是每次计划被重用时增加1。对于其他类型的计划,成本基于计划产生时需要的资源。当这些计划被重用(reuse)时,成本被复置为原始成本。对于non-Adhoc查询,成本被按单位调用的ticks度量,最大值为31。有三个影响因素:I/O,上下文开关和内存。最大值计算如下:
1、每个I/O成本1tick(刻度),最大值19
2、上下文开关每个1tick,最大值8
3、每16页一个tick,最大值4
当没有处于内存压力时,成本在所有计划缓存的总大小达到缓冲池大小的50%不会减少。
sys.dm_os_memory_cache_entries DMV可以展示每个缓存项的当前和原始的成本,以及组成成本的组件:
disk_ios_count, context_switches_count,
pages_allocated_count, original_cost, current_cost
FROM sys.dm_exec_cached_plans p
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
JOIN sys.dm_os_memory_cache_entries e
ON p.memory_object_address = e.memory_object_address
WHERE cacheobjtype = 'Compiled Plan'
AND type in ('CACHESTORE_SQLCP', 'CACHESTORE_OBJCP')
ORDER BY objtype desc, usecounts DESC;
sys.dm_os_memory_cache_entries的详细说明,请查看MSDN:http://msdn.microsoft.com/zh-cn/library/ms189488.aspx
■计划缓存中的对象:大图片
到目前为止,除了前面讨论的DMVs和DMFs,还有一个元数据对象叫syscacheobjects仅仅是伪表(pseudotable)。在SQL Server 2005之前的版本,没有Dynamic Management Objects,但是我们有6个这种类型的伪表,包括sysprocesses和syslockinfo,其实并不占用磁盘空间,仅仅当有人执行一个查询而访问他们时才会成形。这与DMO的工作方式类似。
这些对象在SQL Server 2008中仍然可用。在SQL Server 2000中,这些伪表只在master数据库中可用。或者当引用它们时使用完整的修饰符。在SQL Server 2008中,你可以从任何数据库访问syscacheobjects,只要使用sys schema作为一个修饰符。
关于sys.syscacheobjects视图的详细用法,请参看:http://msdn.microsoft.com/zh-cn/library/ms187815.aspx
在SQL Server 2000中,syscacheobjects伪表也包括针对可执行计划的项,即cacheobjectype列有一个值Executable Plan。在SQL Server 2008中,因为可执行计划被看作从属对象而从已编译计划中完全脱离存储,通过sys.syscacheobjects视图将无法访问。为了访问可执行计划,你必须通过sys.dm_exec_cached_plan_dependent_objects函数,传递一个参数plan_handle。
作为sys.syscacheobjects视图的一个替代方案,这个兼容性视图在后续版本中将不复存在。你可以从SQL Server Management Objects中提取同样的信息。下列脚本在master中创建了一个视图sp_cacheobjects。注意:在master中创建的以sp_开头的任何对象,都可以被任何数据库不需要对象地访问。还有一个好处是,创建的对象可以按你的需要定制它。例如,可以使用一个或多个outer apply,用连接使用sys.dm_exec_query_plan函数,得到缓存中每个计划的XML计划。
GO
CREATE VIEW sp_cacheobjects
(bucketid, cacheobjtype, objtype, objid, dbid, dbidexec, uid,
refcounts, usecounts, pagesused, setopts, langid, dateformat,
status, lasttime, maxexectime, avgexectime, lastreads,
lastwrites, sqlbytes, sql)
AS
SELECT pvt.bucketid,
CONVERT(nvarchar(18), pvt.cacheobjtype) AS cacheobjtype,
pvt.objtype,
CONVERT(int, pvt.objectid) AS object_id,
CONVERT(smallint, pvt.dbid) AS dbid,
CONVERT(smallint, pvt.dbid_execute) AS execute_dbid,
CONVERT(smallint, pvt.user_id) AS user_id,
pvt.refcounts, pvt.usecounts,
pvt.size_in_bytes / 8192 AS size_in_bytes,
CONVERT(int, pvt.set_options) AS setopts,
CONVERT(smallint, pvt.language_id) AS langid,
CONVERT(smallint, pvt.date_format) AS date_format,
CONVERT(int, pvt.status) AS status,
CONVERT(bigint, 0),
CONVERT(bigint, 0),
CONVERT(bigint, 0),
CONVERT(bigint, 0),
CONVERT(bigint, 0),
CONVERT(int, LEN(CONVERT(nvarchar(max), fgs.text)) * 2),
CONVERT(nvarchar(3900), fgs.text)
FROM (SELECT ecp.*, epa.attribute, epa.value
FROM sys.dm_exec_cached_plans ecp
OUTER APPLY
sys.dm_exec_plan_attributes(ecp.plan_handle) epa) AS ecpa
PIVOT (MAX(ecpa.value) for ecpa.attribute IN
("set_options", "objectid", "dbid",
"dbid_execute", "user_id", "language_id",
"date_format", "status")) AS pvt
OUTER APPLY sys.dm_exec_sql_text(pvt.plan_handle) fgs
也许你注意到在几个输出列已经被硬编码为0,因为这些列在SQL Server 2005/2008中已经不再保留。特别,这些列被用于为缓存计划保存性能信息的报告列。在SQL Server 2000中,这些性能数据被为每个批处理保留。在后续版本中,它被保留在语句级,并且可以通过sys.dm_exec_query_ststs可用。为了兼容sys.syscacheobjects视图,新的视图必须在特定列位置返回某些值。如果你选择定制视图,你可以选择移除这些列。
至此,计划缓存的内部操作告一段落,我们将继续关注计划缓存的时机和计划缓存冲突。