分配页失败:FAIL_PAGE_ALLOCATION 1

kai*_*lyn 7 sql-server memory sql-server-2012

我们使用的是 Windows 2012 R2 上的 SQL Server 2012 SP3 Enterprise Edition。

我在 sql 日志中看到了这些错误:

分配页失败:FAIL_PAGE_ALLOCATION 1

2016-06-14 04:28:27.44 spid175 错误:701,严重性:17,状态:123。

2016-06-14 04:28:27.44 spid175 资源池“默认”中的系统内存不足,无法运行此查询。

2016-06-14 04:28:27.44 服务器错误:17300,严重性:16,状态:1。(参数:)。错误以简洁模式打印,因为格式化过程中出现错误。跟踪、ETW、通知等被跳过。

2016-06-14 04:28:27.44 服务器错误:17300,严重性:16,状态:1。(参数:)。错误以简洁模式打印,因为格式化过程中出现错误。跟踪、ETW、通知等被跳过。

2016-06-14 04:28:27.44 spid131 错误:701,严重性:17,状态:123。

据我所知,它似乎在某个时候耗尽了内存。
有没有办法找出导致它内存不足的原因?
MEMORYCLERK_SQLQERESERVATIONS是相当高的,没有人知道那是什么呢?

MEMORYCLERK_SQLQERESERVATIONS (node 0)           KB
---------------------------------------- ----------
VM Reserved                                       0
VM Committed                                      0
Locked Pages Allocated                            0
SM Reserved                                       0
SM Committed                                      0
Pages Allocated                            22599824
Run Code Online (Sandbox Code Playgroud)

编辑:我们在服务器上有 32 个内存,28 个分配给 SQL Server。最大内存设置为 28gb,最小服务器内存为 8gb。

这是 ErrorLog 输出的链接: ErrorLog

这是 sys.dm_os_process_memory 输出的链接: 查询输出

链接到等待类型: WaitTypes

我在内存使用量似乎更多的时候运行了它:

SELECT * FROM sys.dm_exec_query_memory_grants 其中 grant_time 为空

结果:memory_grants

SELECT mg.granted_memory_kb, mg.session_id, t.text, qp.query_plan FROM sys.dm_exec_query_memory_grants AS mg CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) AS t CROSS APPLY sys.dm_exec_query_handle (mg.plan)选项 (MAXDOP 1) 结果:QueryMemGrants

SELECT top 50 t.text, cp.objtype ,qp.query_plan, cp.usecounts, cp.size_in_bytes >as [Bytes Used in Cache] FROM sys.dm_exec_cached_plans AS cp JOIN sys.dm_exec_query_stats AS qs ON cp.plan_handle = qs CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS t WHERE qp.query_plan.exist('declare namespace >n="http://schemas.microsoft.com/sqlserver/ 2004/07/showplan";>//n:MemoryFractions') = 1 个 cp.size_in_bytes desc 选项 (MAXDOP 1)

结果:CachedMemGrants

从 sys.dm_exec_requests 中选择 grant_query_memory,session_id,command 结果:dm_exec_requests

来自正在运行的查询之一的查询计划的 XML: QueryPlan

Sha*_*nky 5

错误日志的输出有dbcc memorystatus转储,我注意到的是

Process/System Counts                         Value(in Bytes)
---------------------------------------- ----------
Available Physical Memory                1217605632---1.1 G
Available Virtual Memory                 140627167866880
Available Paging File                    5656502272
Working Set                               305238016
Percent of Committed Memory in WS                99
Page Faults                                27923310
System physical memory high                       0
System physical memory low                        0
Process physical memory low                       1--Memory Low
Process virtual memory low                        0
2016-06-14 04:28:27.41 Server    
Run Code Online (Sandbox Code Playgroud)

请注意可用的物理内存非常低。缓冲池中几乎没有内存

关于消耗更多内存的职员

MEMORYCLERK_SQLQERESERVATIONS (node 0)           KB
---------------------------------------- ----------
VM Reserved                                       0
VM Committed                                      0
Locked Pages Allocated                            0
SM Reserved                                       0
SM Committed                                      0
Pages Allocated                            22599824  --21.5 G

Page Life Expectancy                             64
Run Code Online (Sandbox Code Playgroud)

现在在最大服务器内存为 28 G 的服务器上,如果MEMORYCLERK_SQLQERESERVATIONS占用 21.5 G 那绝对是一个问题。这就是导致 OOM 条件的原因。

什么是 MEMORYCLERK_SQLQERESERVATIONS

这是 SQL Server 中的内存管理员,它跟踪分配给查询的内存,该查询涉及执行期间的排序散列操作。这些运算符可能是查询的最大内存使用者。

为什么会出现 OOM 错误

当执行涉及排序和散列操作的查询时,它会根据包含排序或散列运算符的原始查询计划提出保留请求。然后,当查询执行时,它会请求内存,SQL Server 将根据内存可用性部分或全部授予该请求。有一个名为“MEMORYCLERK_SQLQERESERVATIONS”的内存管理员(会计)跟踪此类请求的内存分配。现在在您的场景中可能会发生以下情况

  1. 查询请求太多的内存授予 SQL Server 只能为其提供有限的数量,这个有限的数量称为“必需内存”,因此它开始执行并在执行查询时,因为内存需求很大而 SQL Server 不能提供它,因为资源池中没有内存,查询失败并出现 OOM 错误。查询运行时所需的内存称为“附加内存”

  2. SQL Server 2012 Sp1 CU4中修复了错误,其中查询请求了大量内存授予,导致它非常慢或随后因 OOM 错误而失败。考虑到 QEReservations 占用了所有缓冲池的事实,不能排除错误重新出现的可能性

  3. 由于店员已经占用了 90% 的内存。新查询所需的内存不可用,查询失败并出现 OOM 错误。

  4. 您的表和索引具有倾斜的统计信息,这迫使优化器构建次优计划,导致它请求比实际需要更多的内存授予,从而产生问题。

  5. 最后,在 SQL Server 上运行的查询需要一些认真的调整。


根据此Blogs.msdn 文章

开发人员实际上可以对排序/散列操作做些什么?

说到重写查询,这里有一些需要在查询中寻找的可能会导致大量内存授权的事情。

查询将使用 SORT 运算符的原因(并非所有包含列表):

ORDER BY (T-SQL)

GROUP BY (T-SQL)

DISTINCT (T-SQL)

Merge Join operator selected by the optimizer and one of the inputs of the Merge join has to be sorted because a clustered index is
Run Code Online (Sandbox Code Playgroud)

在该列上不可用。

查询将使用哈希匹配运算符的原因(并非所有包含列表):

JOIN (T-SQL) – if SQL ends up performing a Hash Join. Typically, lack of good indexes may lead to the most expensive of join operators
Run Code Online (Sandbox Code Playgroud)

– 哈希连接。查看查询计划。

DISTINCT (T-SQL) – a Hash Aggregate could be used to perform the distinct. Look at query plan.

SUM/AVG/MAX/MIN (T-SQL)– any aggregate operation could potentially be performed as a Hash Aggregate . Look at query plan.

UNION – a Hash Aggregate could be used to remove the duplicates.
Run Code Online (Sandbox Code Playgroud)

为了进一步了解这个问题,我需要您将以下查询的输出添加到您的问题中。我还希望您添加Paul Randal Wait stats query 的输出。查询来源为This Blog,建议您阅读该博客。

SELECT * FROM sys.dm_exec_query_memory_grants where grant_time is null

--Find who uses the most query memory grant:

SELECT mg.granted_memory_kb, mg.session_id, t.text, qp.query_plan
FROM sys.dm_exec_query_memory_grants AS mg
CROSS APPLY sys.dm_exec_sql_text(mg.sql_handle) AS t
CROSS APPLY sys.dm_exec_query_plan(mg.plan_handle) AS qp
ORDER BY 1 DESC OPTION (MAXDOP 1)

--Search cache for queries with memory grants:

SELECT t.text, cp.objtype,qp.query_plan
FROM sys.dm_exec_cached_plans AS cp
JOIN sys.dm_exec_query_stats AS qs ON cp.plan_handle = qs.plan_handle
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS t
WHERE qp.query_plan.exist(‘declare namespace n=”http://schemas.microsoft.com/sqlserver/2004/07/showplan“; //n:MemoryFractions’) = 1
Run Code Online (Sandbox Code Playgroud)

我希望您检查系统上运行的查询还有其他几件事。

Select granted_query_memory,session_id,command from sys.dm_exec_requests
Run Code Online (Sandbox Code Playgroud)

这将显示为系统上运行的查询授予了多少内存。

如果您可以看到 XML 实际执行计划,您MemoryGrant=xxxxx可以为昂贵的查询收集此值。

以上所有内容将向我们展示查询是否存在问题或其他一些问题,即为什么它需要如此多的内存来执行。


编辑

从您粘贴的各种查询输出中。

在此处输入图片说明

您可以看到requested_memory_kb大量查询大约为 5G,这是大内存授权,理想情况下应该是几 MB。请注意,required_memory_kb它只有大约 5 MB 并且granted_query_memory为 NULL,这是因为由于内存压力,SQL Server 只能提供最小内存来启动查询,但无法为查询执行提供额外的内存,导致查询失败并出现 OOM 错误。

请求巨大内存的查询的查询成本也很高,这让我相信要么统计数据有偏差,要么查询写得不好。其他可能性是适当索引不支持查询。请求如此巨大的内存授予的查询数量在数量上很好。

在此处输入图片说明

对于上述查询,请参阅granted_query_memory全部以 GB 为单位。运行的前 3 个查询使用了大约 15 G 的内存,几乎使用了 50% 的内存。在 SQL Server 中,数以百万计的进程运行以某种方式需要内存,因此您可以查看 3 个查询是否使用了 50% 的可用内存,因此必然会发生 OOM 问题。

解决方案

您应该认真考虑调整上面屏幕截图中的前 4 个查询

确保你至少每周运行一次索引重建和统计更新,这样倾斜的统计数据不会迫使优化器产生错误的计划。

使用资源调控器并创建资源池和工作负载组,并运行请求在此池中授予大内存的查询。您可以使用参数限制内存请求request_max_memory_grant_percentage此博客中显示了一个示例。在您调整所有查询之前,这只是替代方法。