内存优化 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)分配器或表堆中显示已分配和已用字节的高值,Object_ID其中XTP_Object_ID,以及是Index_IDNULL

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

  • 方案 4

    不断增加“VARHEAP\Storage 内部堆”的 XTP 数据库内存消耗导致出现内存不足错误 41805。

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

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

  • 方案 5

    DMV tempdb.sys.dm_db_xtp_memory_consumers 在表堆中显示已分配或已用字节的高值,其中Object_ID不是NULL。 此问题的最常见原因是在临时表上使用 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 结构获得更多内存时,内存会转移到 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工作负荷和/或使用临时对象的长时间未完成事务。