排查 Azure SQL 数据库的内存不足错误

适用于:Azure SQL 数据库

当 SQL 数据库引擎无法分配足够的内存来运行查询时,你可能会看到错误消息。 出错的原因多种多样,包括所选服务目标的限制、聚合工作负载内存需求,以及查询内存需求。

有关 Azure SQL 数据库内存资源限制的详细信息,请参阅 Azure SQL 数据库中的资源管理

有关排查 SQL Server 中内存不足问题的详细信息,请参阅 MSSQLSERVER_701

请尝试以下调查途径来应对:

  • 错误代码 701,错误消息为“资源池 '%ls' 中的系统内存不足,无法运行此查询。”
  • 错误代码 802,错误消息为“缓冲池中的可用内存不足”。

查看内存不足事件

如果遇到内存不足错误,请查看 sys.dm_os_out_of_memory_events。 此视图包含预测的内存不足原因信息,这些信息由一种启发式算法确定,可信度有限。

SELECT * FROM sys.dm_os_out_of_memory_events ORDER BY event_time DESC;  

调查内存分配

如果 Azure SQL 数据库中仍旧出现内存不足错误,请至少考虑在 Azure 门户中暂时提高数据库的服务级别目标。

如果内存不足错误仍旧存在,请使用以下查询来查找可能导致内存不足状况的异常高的查询内存分配值。 在出错的数据库(而不是 Azure SQL 逻辑服务器的 master 数据库)中运行以下示例查询。

使用 DMV 查看内存不足事件

sys.dm_os_out_of_memory_events 实现了对 Azure SQL 数据库中内存不足 (OOM) 事件及其原因的可见性。 summarized_oom_snapshot 扩展事件是现有 system_health 事件会话的一部分,用于简化检测。 有关详细信息,请参阅 sys.dm_os_out_of_memory_events博客文章:排查数据库引擎中内存不足错误的新方法

使用 DMV 查看内存分配器

首先进行广泛的调查,通过查看针对内存分配器的内存分配,了解最近是否发生了内存不足错误。 内存监控器是此 Azure SQL 数据库引擎的内部组件。 在页面分配方面,排名靠前的内存分配器可能有助于了解 SQL Server 的哪种查询或功能消耗了最多内存。

SELECT [type], [name], pages_kb, virtual_memory_committed_kb
FROM sys.dm_os_memory_clerks
WHERE memory_node_id <> 64 -- ignore Dedicated Admin Connection (DAC) node
ORDER BY pages_kb DESC;
GO
SELECT [type], [name], pages_kb, virtual_memory_committed_kb
FROM sys.dm_os_memory_clerks
WHERE memory_node_id <> 64 -- ignore Dedicated Admin Connection (DAC) node
ORDER BY virtual_memory_committed_kb DESC;
  • 对于一些常见的内存分配器(例如 MEMORYCLERK_SQLQERESERVATIONS),可以通过识别内存授予较大的查询并使用更好的索引编制和索引优化提高其性能,来以最佳方式解决其问题。
  • 虽然 OBJECTSTORE_LOCK_MANAGER 与内存授予无关,但当查询声明许多锁(例如,由于锁升级被禁用或事务非常大而导致查询声明许多锁)时,预计它会很高。
  • 一些分配器预计使用率最高:MEMORYCLERK_SQLBUFFERPOOL 几乎总是使用率最高的分配器,而 CACHESTORE_COLUMNSTOREOBJECTPOOL 的使用率则会在使用列存储索引时高。 预计这些分配器的使用率会达到最高水平。

有关内存分配器类型的详细信息,请参阅 sys.dm_os_memory_clerks

使用 DMV 调查活动查询

在大多数情况下,失败的查询不是此错误的原因。

以下针对 Azure SQL 数据库的示例查询返回有关当前正在持有或等待内存授予的事务的重要信息。 将确定要检查并优化性能的前几个查询定为目标,评估它们的执行是否符合预期。 考虑内存密集型报告查询或维护操作的时间安排。

--Active requests with memory grants
SELECT
--Session data 
  s.[session_id], s.open_transaction_count
--Memory usage
, r.granted_query_memory, mg.grant_time, mg.requested_memory_kb, mg.granted_memory_kb
, mg.required_memory_kb, mg.used_memory_kb, mg.max_used_memory_kb     
--Query 
, query_text = t.text, input_buffer = ib.event_info, query_plan_xml = qp.query_plan
, request_row_count = r.row_count, session_row_count = s.row_count
--Session history and status
, s.last_request_start_time, s.last_request_end_time, s.reads, s.writes, s.logical_reads
, session_status = s.[status], request_status = r.status
--Session connection information
, s.host_name, s.program_name, s.login_name, s.client_interface_name, s.is_user_process
FROM sys.dm_exec_sessions s 
LEFT OUTER JOIN sys.dm_exec_requests AS r 
    ON r.[session_id] = s.[session_id]
LEFT OUTER JOIN sys.dm_exec_query_memory_grants AS mg 
    ON mg.[session_id] = s.[session_id]
OUTER APPLY sys.dm_exec_sql_text (r.[sql_handle]) AS t
OUTER APPLY sys.dm_exec_input_buffer(s.[session_id], NULL) AS ib 
OUTER APPLY sys.dm_exec_query_plan (r.[plan_handle]) AS qp 
WHERE mg.granted_memory_kb > 0
ORDER BY mg.granted_memory_kb desc, mg.requested_memory_kb desc;

你可能会决定使用 KILL 语句来停止当前正在执行的查询,该查询正在接受或等待大量内存授予。 请慎用此语句,尤其是当关键进程正在运行时。 有关详细信息,请参阅 KILL (Transact-SQL)

使用查询存储调查过去的查询内存使用情况

前面的示例查询仅报告实时查询结果,而以下查询使用查询存储来返回有关以往查询执行的信息。 这有助于调查过去发生的内存不足错误。

以下针对 Azure SQL 数据库的示例查询返回有关查询存储记录的查询执行的重要信息。 将确定要检查并优化性能的前几个查询定为目标,评估它们的执行是否符合预期。 请注意 qsp.last_execution_time 的时间筛选器,它会将结果限制为最近的历史记录。 可以根据环境调整 TOP 子句以生成更多或更少的结果。

SELECT TOP 10 PERCENT --limit results
  a.plan_id, query_id, plan_group_id, query_sql_text
, query_plan = TRY_CAST(query_plan as XML)
, avg_query_max_used_memory
, min_query_max_used_memory
, max_query_max_used_memory
, last_query_max_used_memory 
, last_execution_time  
, query_count_executions
    FROM (
    SELECT 
      qsp.plan_id, qsp.query_id, qsp.plan_group_id, qsp.query_plan, qsqt.query_sql_text
    , last_execution_time = MAX(qsp.last_execution_time)
    , query_count_executions = SUM(qsrs.count_executions) 
    , avg_query_max_used_memory = AVG(qsrs.avg_query_max_used_memory)
    , min_query_max_used_memory = MIN(qsrs.min_query_max_used_memory)
    , max_query_max_used_memory = MAX(qsrs.max_query_max_used_memory)
    , last_query_max_used_memory = MAX(qsrs_latest.last_query_max_used_memory) --only from latest result
    FROM sys.query_store_plan AS qsp 
    INNER JOIN sys.query_store_query AS qsq
        ON qsp.query_id = qsq.query_id
    INNER JOIN sys.query_store_query_text AS qsqt
        ON qsq.query_text_id = qsqt.query_text_id 
    INNER JOIN sys.query_store_runtime_stats AS qsrs
        ON qsp.plan_id = qsrs.plan_id 
    INNER JOIN (SELECT plan_id
            , last_query_max_used_memory 
            , rownum = ROW_NUMBER() OVER (PARTITION BY plan_id ORDER BY last_execution_time DESC)
            FROM sys.query_store_runtime_stats qsrs) AS qsrs_latest
        ON qsrs_latest.plan_id = qsp.plan_id
        AND qsrs_latest.rownum = 1 --use latest last_query_max_used_memory per plan_id
    WHERE DATEADD(hour, -24, sysdatetime()) < qsp.last_execution_time --past 24 hours only
    AND qsrs_latest.last_query_max_used_memory > 0
    GROUP BY qsp.plan_id, qsp.query_id, qsp.plan_group_id, qsp.query_plan, qsqt.query_sql_text
    ) AS a
ORDER BY max_query_max_used_memory DESC, avg_query_max_used_memory DESC;

扩展的事件

除了上述信息之外,捕获服务器活动的跟踪记录能更有效地调查 Azure SQL 数据库中的内存不足问题。

在 SQL Server 中捕获跟踪有两种方法:扩展事件 (XEvent) 和探查器跟踪。 但是,SQL Server Profiler 是已被弃用的跟踪技术,Azure SQL 数据库不支持这种技术。 扩展事件是一种较新的跟踪技术,它支持更多的通用性,对观察到的系统的影响较小,并且它的接口集成到 SQL Server Management Studio (SSMS) 中。 有关查询 Azure SQL 数据库中的扩展事件的详细信息,请参阅 Azure SQL 数据库中的扩展事件

请参阅说明如何在 SSMS 中使用扩展事件新建会话向导的文档。 但对于 Azure SQL 数据库,SSMS 在对象资源管理器中的每个数据库下都提供了扩展事件子文件夹。 使用扩展事件会话捕获这些有用的事件,并确定生成这些事件的查询:

  • 分类错误:

    • error_reported
    • exchange_spill
    • hash_spill_details
  • 分类执行:

    • excessive_non_grant_memory_used
  • 分类内存:

    • query_memory_grant_blocking
    • query_memory_grant_usage
  • summarized_oom_snapshot

    捕获内存授予块、内存授予溢出或过多的内存授予可能是针对查询突然占用比过去更多的内存的潜在线索,也可能是对现有工作负荷中出现内存不足错误的潜在解释。 summarized_oom_snapshot 扩展事件是现有 system_health 事件会话的一部分,用于简化检测。 有关详细信息,请参阅博客文章:排查数据库引擎中内存不足错误的新方法

内存中 OLTP 内存不足

如果使用内存中 OLTP,可能会遇到“Error code 41805: There is insufficient memory in the resource pool '%ls' to run this operation”。 减小内存优化表中的数据量和内存优化表值参数,或将数据库扩展到更高的服务目标以获得更多内存。 有关 SQL Server 内存中 OLTP 内存不足问题的详细信息,请参阅解决内存不足问题

获取 Azure SQL 数据库支持

如果 Azure SQL 数据库中仍旧出现内存不足错误,请在 Azure 支持站点上选择“获取支持”以提交 Azure 支持请求。