无法回收内存中 OLTP 中索引未使用的内存

Zik*_*ato 6 sql-server memory memory-optimized-tables sql-server-2019

重现问题的步骤

创建具有内存优化文件组和容器的数据库 使用非集群 pk 创建仅模式内存表 模拟插入和删除活动。我的结果是我有高索引未使用的内存,不会下降。

 USE master
 go
 DROP DATABASE IF EXISTS MemoryOptimizedTest
 CREATE DATABASE MemoryOptimizedTest
 GO
 USE MemoryOptimizedTest
 GO
 ALTER DATABASE MemoryOptimizedTest 
 ADD FILEGROUP imoltp_mod CONTAINS MEMORY_OPTIMIZED_DATA
 GO 
 
 ALTER DATABASE MemoryOptimizedTest ADD FILE (name='imoltp_mod1', filename='c:\imoltp_mod1') TO FILEGROUP imoltp_mod
 GO
 
 
 DROP TABLE IF EXISTS dbo.MyCache
 CREATE TABLE dbo.MyCache
 (
    PK int NOT NULL, 
    SecondInt int NOT NULL,
    ThirdInt int NOT NULL,
     CONSTRAINT PK_MyCache PRIMARY KEY NONCLUSTERED (PK)
 ) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)
 
 go

/* Generate activity and monitor table size */
USE MemoryOptimizedTest
go


SELECT
    object_id,
    OBJECT_SCHEMA_NAME(object_id) + '.' + OBJECT_NAME(object_id) AS Table_Name,
    memory_allocated_for_table_kb,
    memory_used_by_table_kb,
    memory_allocated_for_indexes_kb,
    memory_used_by_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats
WHERE OBJECT_ID = OBJECT_ID('dbo.MyCache')

;WITH
  L0   AS(SELECT 1 AS c UNION ALL SELECT 1),
  L1   AS(SELECT 1 AS c FROM L0 CROSS JOIN L0 AS B),
  L2   AS(SELECT 1 AS c FROM L1 CROSS JOIN L1 AS B),
  L3   AS(SELECT 1 AS c FROM L2 CROSS JOIN L2 AS B),
  L4   AS(SELECT 1 AS c FROM L3 CROSS JOIN L3 AS B),
  L5   AS(SELECT 1 AS c FROM L4 CROSS JOIN L4 AS B),
  Nums AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L5)
, tally AS (SELECT TOP (10000) n FROM Nums ORDER BY n)
INSERT INTO dbo.MyCache (PK, SecondInt, ThirdInt)
SELECT 
    n
    , n+1
    , n+2
FROM tally 

WAITFOR DELAY '00:00:02'
DELETE FROM dbo.MyCache

GO 50
Run Code Online (Sandbox Code Playgroud)

当我在具有 16 GB 最大内存和 1.5 GB 可用内存的本地计算机 Microsoft SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64) Developer Edition 上运行它时,memory_alulated_for_indexes_kb 正常波动。

当我在开发环境 Microsoft SQL Server 2019 (RTM-CU7) (KB4570012) - 15.0.4063.15 (X64) Enterprise Edition 2 TB 最大内存,220 GB 可用内存上运行它时

memory_alated_for_indexes_kb 只会增长。我已经模拟了表的活动几个小时,并且索引已用内存 = 0.24 MB,索引未使用内存 = 385 MB,并且它不会下降。

垃圾收集器根据 XTP 垃圾收集中的 PerfMon Sweep 每秒删除的过期行数运行。

我在某处读到,垃圾收集器在面临内存压力之前不会释放空间,但它会保留这么多未使用的内存,这似乎很奇怪。

编辑:我使用资源池作为保存内存表的数据库。百分之一是我能达到的最低水平。我用另一个表填充了内存,占用了资源池的 99%,但内存分配的索引仍然不会下降。是否有交易活动。没有任何活动事务会阻塞 GC,我已经等待了 30 多分钟。

我无法接受任何当前答案,因为我的问题仍未得到解答。请注意,表行已被清理,但索引并未被清理。

Low*_*n M 5

我在某处读到,垃圾收集器在面临内存压力之前不会释放空间,但它会保留这么多未使用的内存,这似乎很奇怪。

不过,这很奇怪吗?为什么现在要进行垃圾收集,因为它可以推迟到以后:)

文档中,确认了您的想法(强调我的):

用户事务提交后,它会识别与其运行的调度程序关联的所有排队项目,然后释放内存。如果调度程序上的垃圾收集队列为空,它将在当前 NUMA 节点中搜索任何非空队列。如果事务活动较低并且存在内存压力,则主垃圾收集线程可以访问任何队列中的垃圾收集行。如果(例如)删除大量行后没有事务活动并且没有内存压力,则删除的行将不会被垃圾收集,直到事务活动恢复或存在内存压力。

您的测试工作负载似乎很小,如果它不足以触发任何收集,我不会感到惊讶,特别是考虑到该系统的内存资源有多强大。

有了 2TB 内存,我不会为此目的而使用 < 500MB 的内存,也不会担心这一点,直到它成为一个实际问题