授予内存
授予内存(Grant Memory)是专门用于执行排序操作和哈希操作的内存,由于排序操作(Sort)需要临时存储排序的中间结果集,哈希连接(Hash Join)和哈希聚合(Hash Aggregation)需要创建临时的哈希表,这些数据都需要全部缓存或部分缓存到内存中,因此,在查询请求(Request)真正执行之前,执行计划必须向系统申请一定数量的内存资源,这些内存资源叫做授予内存。如果SQL Server引擎不能给该执行计划分配其申请的授予内存,那么它不会开始执行,处于等待资源信号(RESOURCE_SEMAPHORE)的状态。
SQL Server使用资源信号标(Resource Semaphore)追踪系统分配给查询计划的授予内存的数量,资源信号工作的流程是:数据库引擎接收到一个查询请求(Request),该查询请求包含排序或哈希操作,因此,需要申请授予内存,如果,SQL Server引擎用完了内存空间,此时,SQL Server引擎利用资源信号,使查询请求转变为等待内存变为可用的状态,这样做的目的是使该执行计划不会因为内存不足而抛出错误。等到其他查询请求释放内存,SQL Server引擎拥有足够的内存之后,SQL Server引擎再把内存分配给查询请求,真正开始执行该查询请求。
RESOURCE_SEMAPHORE: occurs when a query memory request cannot be granted immediately due to other concurrent queries. High waits and wait times may indicate excessive number of concurrent queries, or excessive memory request amounts.
授予内存(Granted Memory),查询执行保留(QE Reservations),查询执行内存(Query Execution Memory),工作内存(Workspace memory),内存保留(Memory Reservations),这些复杂的概念,归根到底都在说同一个内存消耗场景:在执行排序,哈希,创建索引,大容量复制(Bulk Copy)操作时,查询请求需要内存临时存储中间结果。
在一个查询请求的生命周期内,它可能需要申请不同的内存,这取决于查询请求做的是什么操作,例如,数据库引擎接收到一个查询请求,
- 首先数据库引擎需要解析和编译查询语句,它需要分配用于编译或优化的内存;一旦查询请求编译成功,这部分内存被释放。
- 然后,生成的执行计划被缓存到内存中,这部分缓存称作计划缓存(Plan Cache),SQL Server会保持查询请求的计划缓存,直到SQL Server重启,内存不足,或者执行计划被重编译。
- 最后,如果执行计划需要执行排序操作或哈希操作(哈希连接或哈希聚合),那么执行计划需要向系统申请用于存储临时排序的结果集或哈希表的内存,这部分内存称作授予内存。
- 注意:如果中间结果过大,SQL Server执行引擎会把部分数据转存到tempdb(硬盘)中,这样,中间数据就同时存储在内存和硬盘两个地方。
内存保留(Memory Reservations)或者查询执行保留(Query Execution (QE) Reservations)是查询计划执行前向系统请求保留的内存。当一个查询需要内存执行排序或哈希操作时,它会基于原始的查询计划(包含排序和哈希操作)向系统发送保留内存的请求(reservation request ),然后,当查询开始执行时,它请求内存,SQL Server授予内存。有一个内存书记 MEMORYCLERK_SQLQERESERVATIONS 用于记录分配的QE保留内存。
内存授予(Memory Grants)是一个正在执行的查询请求被系统授予的、用于执行排序或哈希操作的内存。如果查询请求向系统申请授予内存,系统却没有足够的内存时,查询请求必须等待,直到系统分配足够的授予内存。
一,授予内存
如果一个查询请求需要等待分配授予内存,那么可以通过系统视图:sys.dm_exec_query_memory_grants 查看等待系统分配授予内存的查询请求(Request),及它的关于授予内存的信息。如果一个查询请求不需要等待分配授予内存,或者不需要授予内存,那么它不会出现在该视图中。在编译查询请求时,SQL Server首先评估内存的使用量(ideal_memory_kb),查询计划在执行之前,需要向SQL Server申请内存(requested_memory_kb),SQL Server 根据系统系统内存的使用情况,分配一定数量的物理内存(granted_memory_kb)给该查询请求,查询请求获得授予内存之后开始“真正地”执行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
select g.session_id, g.request_time, g.grant_time, g.wait_time_ms, g.query_cost, g.dop, g.requested_memory_kb, g.granted_memory_kb, g.required_memory_kb, g.used_memory_kb, g.max_used_memory_kb, g.ideal_memory_kb, g.wait_order, g.is_next_candidate, g.group_id, g.pool_id, g.resource_semaphore_id, st.text, p.query_plan from sys.dm_exec_query_memory_grants g outer apply sys.dm_exec_sql_text(g.sql_handle) as st outer apply sys.dm_exec_query_plan(g.plan_handle) as p
视图:sys.dm_exec_requests 的字段:granted_query_memory ,用于表示为该查询请求已经分配的授予内存页的数量,如果一个查询请求正在等待授予内存,那么字段wait_type是RESOURCE_SEMAPHORE。
二,查看资源信号的汇总数据
通过系统视图:sys.dm_exec_query_resource_semaphores 查看当前的所有资源信号的状态,以确定当前系统是否有足够的内存分配给查询请求。该视图汇总系统中所有查询请求的授予内存,为每个资源池(Resource Pool)返回两行,一行是常规的资源信号,另一行是小查询( small-query)的资源信号,所谓小查询的资源信号是指:申请的授予内存小于5MB,查询开销(Query Cost)小于3个开销单位(Cost Unit)。
三,查看内存书记统计的保留内存
在SQL Server中,只有内存书记(Memory Clerk)能够分配内存,任何一个需要使用内存的对象,必须创建自己的Memory Clerk,并使用该Memory clerk来分配内存。
Memory Clerk会记录已经分配内存的数量,SQL Server 使用 ‘MEMORYCLERK_SQLQERESERVATIONS’ 来分配执行排序或哈希操作所需要的内存,可以使用 sys.dm_os_memory_clerks 来查看系统中执行排序或哈希操作时分配的总内存。
select type, pages_kb, virtual_memory_reserved_kb, virtual_memory_committed_kb, shared_memory_reserved_kb, shared_memory_committed_kb, page_size_in_bytes from sys.dm_os_memory_clerks where type = 'MEMORYCLERK_SQLQERESERVATIONS' and memory_node_id<>64
在该视图中,内存节点ID(memory_node_id)为64时,只在DAC中使用,该节点不会关联到任何物理内存节点(Physical Memory Node),仅是为了支持DAC而专门设计的一个逻辑内存节点(Logical Memory Node)。
四,数据溢出到tempdb
如果授予内存不足,对查询请求会有什么副作用?授予内存不足,会导致请求数据溢出到tempdb,实际上,是溢出到硬盘,这种警告,是查询语句的性能低下的一个信号。
在执行一个查询语句时,发现 TOP(10) 和 TOP(100)所用时间差距很大。在对其调优时,发现排序操作符(Sort Operator)消耗的时间高达95%,并抛出警告:
Operator used tempdb to spill data during execution with spill level 1
SQL Server 之所以抛出警告,是因为排序操作符的实际值(Actual Number of Rows)明显大于评估值(Estimated Number of Rows),SQL Server引擎根据执行计划的评估值,计算授予内存的数量,然后分配授予内存。在SQL Server 真正进行排序操作时,由于查询请求预先被分配的授予内存少于实际需要的内存,这导致SQL Server必须把中间结果集转存到tempdb中。这种情况虽然不会导致错误,但是会降低查询的性能。如果能使全部数据都在内存中排序,那么就能提高查询语句的性能。
SQL Server引擎计算授予的评估值是根据系统维护的统计信息(statistics)来评估的,如果索引的统计信息(Index Statistics)过期,或者长时间未刷新,这会导致查询优化器(Query Optimizer)低估实际值(Actual number of rows),导致Actual Number of Rows明显大于Estimated Number of Rows,因此,必须刷新索引的统计信息(Statistics),使查询优化器基于正确的统计信息做评估。还可以优化查询语句,使其能够引用索引;或者增加必要的内存,或者创建正确的索引,或者重新编译执行计划。
五,申请内存授予的状态
内存授予只出现在特定的查询语句中,如果一个查询包含排序,Hash等操作,那么该语句在执行之前,必须申请到必需的内存,这意味着,如果数据库引擎不能分配足够的授予内存,那么查询请求将不会执行。视图sys.dm_exec_query_memory_grants 返回当前数据库中正在运行的查询申请内存授予的状态:
select mg.session_id
--,mg.request_id
,mg.resource_semaphore_id
,mg.wait_time_ms
,mg.dop
,mg.requested_memory_kb
,mg.required_memory_kb
,mg.used_memory_kb
,mg.max_used_memory_kb
,mg.ideal_memory_kb
,st.text as entire_sql
from sys.dm_exec_query_memory_grants mg
outer apply sys.dm_exec_sql_text(mg.sql_handle) as st
order by mg.wait_time_ms desc
在申请内存授予时,数据库引擎会发送资源信号(resource semaphore),视图 sys.dm_exec_query_resource_semaphores 返回当前数据库中查询-执行的内存状态,用于检测当前数据库是否有足够的内存,用于所有的查询计划。
当内存紧张时,查询请求申请不到足够的授予内存,处于RESOURCE_SEMAPHORE等待状态。此时,数据库引擎发送资源信号(RESOURCE SEMAPHORE)申请授予内存(Requested Memory)。
当SQL Server实例接收到用户的查询请求时,查询优化器首先创建编译计划(Complied Plan),根据编译计划再创建执行计划(Execution Plan)。查询优化器在创建编译计划时,需要计算查询语句在执行时需要消耗的内存。用于执行查询语句的内存分为必需内存(Required Memory)和额外内存(Additional Memory),必需内存是指SQL Server实例执行Sort或Hash操作必须分配的最小内存,如果没有分配必需内存,查询请求不会执行;额外内存是查询语句用于存储临时的中间数据的内存,如果SQL Server没有足够的内存,数据库引擎把临时数据存储在硬盘中,这会降低查询语句执行的性能。
SQL Server 要授予多少内存,查询请求才能真正开始执行呢?
- Step1,计算需要的内存(Needed Memory):SQL Server计算每个查询需要多少内存才能执行,这通常是必需内存和额外内存之和,当查询请求以并发方式执行时,需要的内存公式是:(Required Memory*DOP)+额外内存。
- Step2,计算请求的内存(Requested Memory):SQL Server检查每个查询请求需要的内存数量是否超出系统的限制,SQL Server减少额外内存的数量,以致于不会超出系统的上限,这个最终的内存数量是查询语句得以执行的请求内存。
- Step3,为查询分配请求内存:SQL Server实例发送资源信号(RESOURCE SEMAPHORE),为查询(Query)语句授予/分配请求的物理内存。
当资源信号发送之后,如果SQL Server实例不能被授予查询的请求内存,那么查询请求处于RESOURCE_SEMAPHORE 等待状态。SQL Server维护一个先入先出( first-come-first-served)的等待队列,当新的查询请求处于RESOURCE_SEMAPHORE 等待状态,SQL Server将该查询放入队列的末尾。一旦SQL Server实例找到足够的空闲内存,那么数据库引擎取出RESOURCE_SEMAPHORE 等待队列顶端的第一个查询,立即授予其申请的请求内存(Requested Memory);该查询获得请求内存之后,开始执行查询任务。如果SQL Server实例长时间有查询处于RESOURCE_SEMAPHORE等待状态,说明SQL Server 面临内存压力。
参考文档:
Memory Meditation: The mysterious SQL Server memory consumer with Many Names
Understanding SQL server memory grant
Identifying and Solving Sort Warnings Problems in SQL Server
Never Ignore a Sort Warning in SQL Server
SQL Server 2012: Sort operator causing tempdb spill
Correct SQL Server TempDB Spills in Query Plans Caused by Outdated Statistics
SpillToTempDb warning and SpillLevel’s mapping to single versus multiple pass