详细信息
Attribute | 值 |
---|---|
产品名称 | SQL Server |
事件 ID | 701 |
事件源 | MSSQLSERVER |
组件 | SQLEngine |
符号名称 | NOSYSMEM |
消息正文 | 系统内存不足,无法运行此查询。 |
注意
本文侧重介绍 SQL Server。 有关 Azure SQL 数据库中的内存不足问题的疑难解答,请参阅排查 Azure SQL 数据库的内存不足错误。
说明
当SQL Server未能分配足够的内存来运行查询时,会发生错误 701。 造成内存不足的因素可能有很多,包括操作系统设置、物理内存可用性、其他组件在 SQL Server 内使用内存或当前工作负荷存在内存限制。 大多数情况下,失败的事务不是引发此错误的原因。 总的来说,原因可以分为三类:
外部或操作系统内存压力
外部压力是指进程外部的组件的内存利用率较高,导致 SQL Server 内存不足。 必须查明系统上的其他应用程序是否正在消耗内存,导致内存可用性较低。 SQL Server 是设计用于通过减少内存使用量来应对操作系统内存压力的为数不多的应用程序之一。 这意味着,如果某个应用程序或驱动程序请求内存,操作系统将向所有应用程序发送一个释放内存的信号,SQL Server 将通过减少其自身的内存使用量来做出响应。 很少有其他应用程序做出响应,因为它们不用于侦听该通知。 因此,如果 SQL 开始减少其内存使用量,则其内存池也会减小,任何需要内存的组件都可能无法获取它。 你将开始收到 701 和其他与内存相关的错误。 有关详细信息,请参阅 SQL Server 内存体系结构
内部内存压力,不来自 SQL Server
内部内存压力是指由 SQL Server 进程内部的因素导致的低内存可用性。 SQL Server 进程内部运行的某些组件可能是 SQL Server 引擎的“外部”组件。 示例包括链接服务器、SQLCLR 组件、扩展过程 (XP) 和 OLE 自动化 (sp_OA*
) 等 DLL。 其他示例包括防病毒或其他安全程序,它们将 DLL 注入进程中以用于监视目的。 这些组件中的任何组件出现问题或设计不佳都可能导致消耗大量内存。 例如,考虑链接服务器,该服务器将来自外部源的 2000 万行数据缓存到 SQL Server 内存中。 就 SQL Server 而言,内存分配器不会报告高内存使用率,但 SQL Server 进程内部消耗的内存将会很高。 例如,链接服务器 DLL 的此内存增长会导致 SQL Server 开始减少其内存使用量(见上文),并造成 SQL Server 内部的组件内存不足,从而导致类似 701 的错误。
内部内存压力,来自 SQL Server 组件
来自 SQL Server 引擎内部的组件的内部内存压力也可能导致错误 701。 有数百个组件在 SQL Server 中分配内存,这些组件通过内存分配器跟踪。 必须确定哪些内存分配器负责最大的内存分配,才能进一步解决此问题。 例如,如果发现 OBJECTSTORE_LOCK_MANAGER 内存分配器显示较大内存分配,则需要进一步了解锁管理器消耗如此多内存的原因。 你可能会发现,有些查询会获取大量的锁,请使用索引对其进行优化,或减少长时间持有锁的事务,或检查是否禁用了锁升级。 每个内存分配器或组件都有一种独特的方式来访问和使用内存。 有关详细信息,请参阅内存分配器类型及其说明。
用户操作
如果偶尔或者短暂出现错误 701,则可能存在可自行解决的短期内存问题。 在这些情况下,你可能不需要采取任何措施。 但是,如果错误在多个连接上多次出现,并且持续数秒或更长时间,请按照以下步骤进行进一步的故障排除。
下面的列表概述了有助于解决内存错误的一般步骤。
诊断工具和捕获
允许收集故障排除数据的诊断工具包括性能监视器、sys.dm_os_memory_clerks 和 DBCC MEMORYSTATUS 。
使用性能监视器配置和收集以下计数器:
- Memory:Available MB
- Process:Working Set
- Process:Private Bytes
- SQL Server:Memory Manager: (all counters)
- SQL Server:Buffer Manager: (all counters)
在受影响的 SQL Server 上收集此查询的定期输出
SELECT pages_kb, type, name, virtual_memory_committed_kb, awe_allocated_kb
FROM sys.dm_os_memory_clerks
ORDER BY pages_kb DESC
Pssdiag 或 SQL LogScout
自动捕获这些数据点的另一种方式是使用 PSSDIAG 或 SQL LogScout 等工具。
- 如果使用 Pssdiag,请配置为捕获“Perfmon”收集器和“自定义诊断\SQL 内存错误”收集器
- 如果使用 SQL LogScout,请配置为捕获“内存”方案
以下各部分介绍了每种方案(外部或内部内存压力)的更详细步骤。
外部压力:诊断和解决方案
-
若要诊断 SQL Server 进程外部系统上的内存不足情况,请收集性能监视器计数器。 通过查看这些计数器,调查除 SQL Server 之外的应用程序或服务是否正在消耗此服务器上的内存:
- Memory:Available MB
- Process:Working Set
- Process:Private Bytes
下面是使用 PowerShell 的示例 Perfmon 日志集合
clear $serverName = $env:COMPUTERNAME $Counters = @( ("\\$serverName" +"\Memory\Available MBytes"), ("\\$serverName" +"\Process(*)\Working Set"), ("\\$serverName" +"\Process(*)\Private Bytes") ) Get-Counter -Counter $Counters -SampleInterval 2 -MaxSamples 1 | ForEach-Object { $_.CounterSamples | ForEach-Object { [pscustomobject]@{ TimeStamp = $_.TimeStamp Path = $_.Path Value = ([Math]::Round($_.CookedValue, 3)) } } }
-
查看系统事件日志,查找与内存相关的错误(例如,虚拟内存不足)。
-
查看应用程序事件日志,了解与应用程序相关的内存问题。
下面是一个示例 PowerShell 脚本,用于查询关键字“memory”的 System 和 Applicaiton 事件日志。 随意使用其他字符串(如“resource”)进行搜索:
Get-EventLog System -ComputerName "$env:COMPUTERNAME" -Message "*memory*" Get-EventLog Application -ComputerName "$env:COMPUTERNAME" -Message "*memory*"
-
解决不太关键的应用程序或服务的任何代码或配置问题,以减少其内存使用量。
-
如果除SQL Server之外的应用程序消耗资源,请尝试停止或重新安排这些应用程序,或考虑在单独的服务器上运行它们。 这些步骤将消除外部内存压力。
内部内存压力,不来自 SQL Server:诊断和解决方案
若要诊断 SQL Server 内部的模块 (DLL) 导致的内部内存压力,请使用以下方法:
-
如果 SQL Server 未使用锁定内存页 (AWE API),那么它的大部分内存都将反映在性能监视器的“Process:Private Bytes”计数器(
SQLServr
实例)中。 SQL Server 引擎中的总体内存使用情况反映在“SQL Server:Memory Manager: Total Server Memory (KB)”计数器中。 如果发现值“Process:Private Bytes”和“SQL Server:Memory Manager: Total Server Memory (KB)”之间存在显著差异,则这种差异可能来自 DLL(链接服务器、XP、SQLCLR 等) 。 例如,如果“Private bytes”为 300 GB,而“Total Server Memory”为 250 GB,则进程中大约 50 GB 的总内存来自 SQL Server 引擎外部 。 -
如果 SQL Server 使用锁定内存页 (AWE API),那么确定问题将更具挑战性,因为性能监视器不提供跟踪单个进程的内存使用情况的 AWE 计数器。 SQL Server 引擎中的总体内存使用情况反映在“SQL Server:Memory Manager: Total Server Memory (KB)”计数器中。 典型的“Process:Private Bytes”值可能在 300 MB 和 1-2 GB 之间变化。 如果发现“Process:Private Bytes”的使用量显著超过此典型使用量,则差异可能来自 DLL(链接服务器、XP、SQLCLR 等)。 例如,如果“Private bytes”计数器为 5-4 GB,并且 SQL Server 使用了锁定内存页 (AWE),则大部分专用字节可能来自 SQL Server 引擎外部。 这是一种近似技术。
-
使用任务列表实用工具确定 SQL Server 空间中加载的所有 DLL:
tasklist /M /FI "IMAGENAME eq sqlservr.exe"
-
还可以使用此查询来检查加载的模块 (DLL),并查看是否存在不需要的内容
SELECT * FROM sys.dm_os_loaded_modules
-
如果怀疑某个链接服务器模块导致消耗大量内存,可以通过禁用“允许进程内”选项将其配置为在进程外运行。 有关详细信息,请参阅创建链接服务器。 并非所有链接服务器 OLEDB 提供程序都可以在进程外运行;有关更多信息,请联系产品制造商。
-
在使用 OLE 自动化对象 (
sp_OA*
) 的极少数情况下,可以通过将“上下文”设置为 4 来将对象配置为在 SQL Server 外部的进程中运行(仅限本地 (.exe) OLE 服务器)。 有关详细信息,请参阅 sp_OACreate。
SQL Server 引擎的内部内存使用:诊断和解决方案
-
开始收集 SQL Server:SQL Server:Buffer Manager 的性能监视器计数器,SQL Server:内存管理器。
-
多次查询 SQL Server 内存分配器 DMV,查看引擎中内存消耗最大的位置:
SELECT pages_kb, type, name, virtual_memory_committed_kb, awe_allocated_kb FROM sys.dm_os_memory_clerks ORDER BY pages_kb DESC
-
或者,可以在看到这些错误消息时观察更详细的 DBCC MEMORYSTATUS 输出及其变化情况。
DBCC MEMORYSTATUS
-
如果在内存分配器中发现了明显的导致错误的因素,请专注于解决该组件的内存消耗具体问题。 下面是几个示例:
- 如果 MEMORYCLERK_SQLQERESERVATIONS 内存分配器正在消耗内存,请确定使用大量内存授予的查询并通过索引对其进行优化,重写它们(例如,删除排序依据),或者应用查询提示。
- 如果缓存了大量临时查询计划,则 CACHESTORE_SQLCP 内存分配器将使用大量内存。 识别其查询计划不能重复使用的非参数化查询,并通过转换为存储过程、使用 sp_executesql 或使用 FORCED 参数化来参数化这些查询。
- 如果对象计划缓存存储 CACHESTORE_OBJCP 消耗了大量内存,请执行以下操作:确定哪些存储过程、函数或触发器使用了大量内存并且可能重新设计应用程序。 通常,这可能由大量数据库或架构引起,每个数据库或架构中都有数百个过程。
- 如果 OBJECTSTORE_LOCK_MANAGER 内存分配器显示了较大内存分配,请确定应用了多个锁的查询,并使用索引对其进行优化。 减少导致在某些隔离级别长时间不释放锁的事务,或检查是否禁用了锁升级。
可能使内存可用的快速缓解措施
以下操作可能会释放一些内存并将其提供给SQL Server:
-
检查以下 SQL Server 内存配置参数,并在可能的情况下考虑增加最大服务器内存:
-
max server memory
-
min server memory
注意不正常的设置。 根据需要更正它们。 满足更高内存要求。 服务器内存配置选项中列出了默认设置。
-
-
如果尚未配置最大服务器内存(尤其是使用锁定内存页),请考虑设置为特定值,以便为操作系统分配一些内存。 请参阅锁定内存页服务器配置选项。
-
检查查询工作负荷:并发会话数、当前正在执行的查询,并查看是否存在可能会暂时停止或移动到另一 SQL Server 的不太关键的应用程序。
-
如果在虚拟机 (VM) 上运行 SQL Server,请确保该 VM 的内存未过量使用。 有关如何为 VM 配置内存的想法,请参阅此博客 虚拟化 – 内存过度使用以及如何在 VM 中检测内存 和 排查 ESX/ESXi 虚拟机性能问题 (内存过度)
-
可以运行以下 DBCC 命令来释放多个SQL Server内存缓存。
-
DBCC FREESYSTEMCACHE
-
DBCC FREESESSIONCACHE
-
DBCC FREEPROCCACHE
-
-
如果使用 Resource Governor,建议检查资源池或工作负荷组设置,看看它们是否对内存限制过大。
-
如果问题仍存在,你将需要进一步调查,可能需要增加服务器资源 (RAM)。