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

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

现象

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

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

在 DMV dm_os_memory_clerks上运行查询时,可以看到分配的页内存对于内存职员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 显示内存使用者类型和VARHEAPLOOKASIDE已用字节的已分配和已用字节的高值。

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

    注意

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

  • 方案 3

    DMV tempdb.sys.dm_db_xtp_memory_consumers 在大型对象(LOB)分配器或表堆中显示已分配和已用字节的高值,XTP_Object_ID其中Object_ID,以及是Index_IDNULL

    解决方法:将 SQL Server 2019 CU16 应用于问题14535149

  • 方案 4

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

    解决方法:14087445已在 SQL Server 17 CU25 及更高版本中识别和解决的问题正在审查中,以便移植到 SQL Server 2019。

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

  • 方案 5

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

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

    对临时表使用 DDL 语句的显式打开事务不允许使用 tempdb 元数据释放表堆和查找堆以供后续事务释放。

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

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

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

  2. 增加 最大服务器内存 ,以允许足够的内存在 tempdb 密集型工作负荷的情况下运行。

  3. 定期运行 sys.sp_xtp_force_gc

  4. 若要防止服务器可能内存不足的情况,可以将 tempdb 绑定到 资源调控器资源池。 例如,使用 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 内存分配器

内存中 OLTP 中的 Lookaside 是一种线程本地内存分配器,可帮助实现快速事务处理。 每个线程对象都包含一组 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 堆感到满意。 已用字节的高值可能是使用临时对象的常量繁重 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 工作负荷和/或长时间运行的开放事务的指示器。