内存优化 tempdb 元数据 (HkTempDB) 内存不足错误

本文提供了解决与内存优化 tempdb 元数据功能相关的内存不足问题的解决方案。

症状

(HkTempDB) 功能启用内存优化tempdb元数据后,可能会看到错误 701,指示分配和SQL Server服务崩溃的内存不足异常tempdb。 此外,你可能会看到 In-Memory OLTP (Hekaton) 的内存 MEMORYCLERK_XTP 正在逐渐或快速增长,并且不会收缩。 随着 XTP 内存在没有上限的情况下增长,SQL Server中看到以下错误消息:

由于资源池“default”中内存不足,不允许数据库“tempdb”的页分配。 有关详细信息,请参阅“http://go.microsoft.com/fwlink/?LinkId=510837”。

DMVdm_os_memory_clerks上运行查询时,可以看到为内存 clerk MEMORYCLERK_XTP分配的页内存较高。 例如:

SELECT type, memory_node_id, pages_kb 
FROM sys.dm_os_memory_clerks
WHERE type = 'MEMORYCLERK_XTP'

结果:

type                    memory_node_id                     pages_kb
------------------------------------------------------------ -------------- --------------------
MEMORYCLERK_XTP         0                                  60104496
MEMORYCLERK_XTP         64                                 0

诊断问题

若要收集数据来诊断问题,请执行以下步骤:

  1. 收集轻量级跟踪或扩展事件 (XEvent) 以了解 tempdb 工作负荷,并找出工作负荷是否有任何长时间运行的显式事务,并在临时表上使用 DDL 语句。

  2. 收集以下 DMV 的输出以进一步分析。

    SELECT * FROM sys.dm_os_memory_clerks
    SELECT * FROM sys.dm_exec_requests
    SELECT * FROM sys.dm_exec_sessions
    
    -- from tempdb
    SELECT * FROM tempdb.sys.dm_xtp_system_memory_consumers 
    SELECT * FROM tempdb.sys.dm_db_xtp_memory_consumers
    
    SELECT * FROM tempdb.sys.dm_xtp_transaction_stats
    SELECT * FROM tempdb.sys.dm_xtp_gc_queue_stats
    SELECT * FROM tempdb.sys.dm_db_xtp_object_stats
    
    SELECT * FROM tempdb.sys.dm_db_xtp_transactions
    SELECT * FROM tempdb.sys.dm_tran_session_transactions
    SELECT * FROM tempdb.sys.dm_tran_database_transactions
    SELECT * FROM tempdb.sys.dm_tran_active_transactions
    

原因和解决方法

通过使用 DMV 验证原因,你可能会看到问题的不同场景。 这些方案可分为以下两类。 若要解决此问题,可以针对每个方案使用相应的解决方法。 有关如何缓解此问题的详细信息,请参阅将内存优化 tempdb 元数据内存保留在检查中的缓解步骤

XTP 内存消耗逐渐增加

  • 方案 1

    DMV tempdb.sys.dm_xtp_system_memory_consumerstempdb.sys.dm_db_xtp_memory_consumers 显示已分配字节和已用字节之间的较大差异。

    解决方法:若要解决此问题,可以在 SQL Server 2019 CU13SQL Server 2022 CU1 或更高版本中运行以下命令,其中包含释放已分配但未使用的字节的新过程sys.sp_xtp_force_gc

    注意

    SQL Server 2022 CU1 开始,只需执行存储过程一次。

    /* Yes, 2 times for both*/
    EXEC sys.sp_xtp_force_gc 'tempdb'
    GO
    EXEC sys.sp_xtp_force_gc 'tempdb'
    GO
    EXEC sys.sp_xtp_force_gc
    GO
    EXEC sys.sp_xtp_force_gc
    
  • 方案 2

    DMV tempdb.sys.dm_xtp_system_memory_consumers 显示内存使用者类型和 VARHEAP 的已分配字节和 LOOKASIDE已用字节的高值。

    解决方法:检查涉及临时表上的 DDL 语句的长时间运行的显式事务,并通过保持事务简短从应用程序端解析。

    注意

    若要在测试环境中重现此问题,可以使用数据定义语言 (DDL) 语句对临时表 () 创建显式 事务 ,并在发生其他活动时将其长时间保持打开状态。

  • 场景 3

    DMV tempdb.sys.dm_db_xtp_memory_consumers 显示大型对象中已分配字节和已用字节的高值 (LOB) 分配器或表堆,其中 Object_IDXTP_Object_IDIndex_IDNULL

    解决方法对问题14535149应用 SQL Server 2019 CU16

  • 场景 4

    不断增加的“VARHEAP\Storage 内部堆”XTP 数据库内存使用者会导致内存不足错误 41805。

    解决方法SQL Server17 CU25 及更高版本中14087445已确定和解决的问题正在审查中,以移植到 2019 SQL Server。

XTP 内存消耗突然激增或快速增加

  • 方案 5

    DMV tempdb.sys.dm_db_xtp_memory_consumers 显示表堆中已分配或已用字节的高值,其中 Object_ID 不是 NULL。 此问题的最常见原因是在临时表 () 上使用 DDL 语句的长时间运行、显式打开的事务。 例如:

    BEGIN TRAN
        CREATE TABLE #T(sn int)
        …
        …
    COMMIT
    

    在临时表上使用 DDL 语句的显式打开事务不允许使用 tempdb 元数据为后续事务释放表堆和 lookaside 堆。

    解决方法:检查涉及临时表上的 DDL 语句的长时间运行的显式事务,并通过保持事务简短从应用程序端解析。

将内存优化 tempdb 元数据内存保留在 检查 中的缓解步骤

  1. 若要避免或解决对临时表使用 DDL 语句的长时间运行的事务,一般指导是使事务保持简短。

  2. 增加 最大服务器内存 ,以允许足够的内存在 tempdb 繁重的工作负载存在时运行。

  3. 定期运行 sys.sp_xtp_force_gc

  4. 若要保护服务器免受潜在的内存不足情况的危害,可以将 tempdb 绑定到Resource Governor资源池。 例如,使用 MAX_MEMORY_PERCENT = 30创建资源池。 然后,使用以下 ALTER SERVER CONFIGURATION 命令将资源池绑定到内存优化 tempdb 元数据。

    ALTER SERVER CONFIGURATION SET MEMORY_OPTIMIZED TEMPDB_METADATA = ON (RESOURCE_POOL = '<PoolName>');
    

    即使已启用内存优化 tempdb 元数据,此更改也需要重启才能生效。 有关更多信息,请参阅:

    警告

    将 HktempDB 绑定到池后,池可能会达到其最大设置,使用的任何查询 tempdb 都可能会失败并出现内存不足错误。 例如:

    由于资源池“HkTempDB”中的内存不足,不允许为数据库“tempdb”分配页。 有关详细信息,请参阅“http://go.microsoft.com/fwlink/?LinkId=510837”。 由于内存压力,XTP 页分配失败:FAIL_PAGE_ALLOCATION 8

    在某些情况下,如果发生内存不足错误,SQL Server服务可能会停止。 若要降低发生这种情况的可能性,请将内存池的 MAX_MEMORY_PERCENT 设置为高值。

  5. 内存优化 tempdb 元数据功能不支持每个工作负载。 例如,对长时间运行的临时表使用显式事务和 DDL 语句将导致上述方案。 如果工作负载中有此类事务,并且无法控制其持续时间,则此功能可能不适合你的环境。 在使用 HkTempDB之前,应进行大量测试。

更多信息

这些部分提供有关内存优化 tempdb 元数据中涉及的一些内存组件的更多详细信息。

Lookaside 内存分配器

In-Memory OLTP 中的 Lookaside 是线程本地内存分配器,可帮助实现快速事务处理。 每个线程对象都包含一个旁视内存分配器的集合。 与每个线程关联的每个外观都有一个预定义的上限,即它可以分配多少内存。 达到限制时,线程将从溢出的共享内存池分配内存, VARHEAP () 。 DMV sys.dm_xtp_system_memory_consumers 聚合 () memory_consumer_type_desc = 'LOOKASIDE' 和共享内存池 (memory_consumer_type_desc = 'VARHEAP'memory_consumer_desc = 'Lookaside heap') 的数据。

系统级使用者:tempdb.sys.dm_xtp_system_memory_consumers

大约 25 种旁观内存使用者类型是上限。 当线程从这些 lookaside 需要更多内存时,内存会溢出到 lookaside 堆,并且对 lookaside 堆感到满意。 已用字节的高值可能是持续繁重 tempdb 工作负荷和/或使用临时对象的长时间运行的开放事务的指标。

-- system memory consumers @ instance  
SELECT memory_consumer_type_desc, memory_consumer_desc, allocated_bytes, used_bytes
FROM sys.dm_xtp_system_memory_consumers 
memory_consumer_type_desc     memory_consumer_desc                   allocated_bytes      used_bytes
------------------------- ------------------------------------------ -------------------- --------------------
VARHEAP                       Lookaside heap                             0                    0
PGPOOL                        256K page pool                             0                    0
PGPOOL                        4K page pool                               0                    0
VARHEAP                       System heap                                458752               448000
LOOKASIDE                     Transaction list element                   0                    0
LOOKASIDE                     Delta tracker cursor                       0                    0
LOOKASIDE                     Transaction delta tracker                  0                    0
LOOKASIDE                     Creation Statement Id Map Entry            0                    0
LOOKASIDE                     Creation Statement Id Map                  0                    0
LOOKASIDE                     Log IO proxy                               0                    0
LOOKASIDE                     Log IO completion                          0                    0
LOOKASIDE                     Sequence object insert row                 0                    0
LOOKASIDE                     Sequence object map entry                  0                    0
LOOKASIDE                     Sequence object values map                 0                    0
LOOKASIDE                     Redo transaction map entry                 0                    0
LOOKASIDE                     Transaction recent rows                    0                    0
LOOKASIDE                     Heap cursor                                0                    0
LOOKASIDE                     Range cursor                               0                    0
LOOKASIDE                     Hash cursor                                0                    0
LOOKASIDE                     Transaction dependent ring buffer          0                    0
LOOKASIDE                     Transaction save-point set entry           0                    0
LOOKASIDE                     Transaction FK validation sets             0                    0
LOOKASIDE                     Transaction partially-inserted rows set    0                    0
LOOKASIDE                     Transaction constraint set                 0                    0
LOOKASIDE                     Transaction save-point set                 0                    0
LOOKASIDE                     Transaction write set                      0                    0
LOOKASIDE                     Transaction scan set                       0                    0
LOOKASIDE                     Transaction read set                       0                    0
LOOKASIDE                     Transaction                                0                    0

数据库级使用者:tempdb.sys.dm_db_xtp_memory_consumers

  • LOB 分配器用于系统表 LOB/行外数据。

  • 表堆用于系统表行。

已用字节的高值可能是使用临时对象的持续繁重 tempdb 工作负荷和/或长时间运行的开放事务的指标。