SQL Server 2019:图形查询的内存性能(可能的内存泄漏)

Ala*_*her 10 memory graph sql-server-2019 memory-manager

我目前正在努力升级到 SQL Server 2019,以便利用其中可用的图形功能。我们的数据库存储文件及其子文件的记录,图形特征使我们能够快速找到文件在任一方向的所有关系。我们当前的开发环境在 Linux 服务器上使用 SQL Server 2019 Standard (15.0.4023.6)。

我在运行图形查询时注意到一个令人担忧的问题。服务器的“内部”资源池在图形查询后似乎没有释放所有资源。如果不选中,这会填满资源池。在重新启动 SQL Server 进程之前,较大的查询将失败。根据服务器负载,这可能会在短短 1-2 小时内发生。这也会填满 tempdb 并威胁填满存储驱动器。在服务器重新启动之前,tempdb 的文件也不能显着缩小/截断。在配置中,'memory.memorylimitmb'没有设置,所以当资源池开始使用默认80%的系统内存(12.8GB,16GB系统内存)中的大部分时,就会出现这个问题

要在演示数据库中设置表:

CREATE TABLE FileNode (ID BIGINT NOT NULL CONSTRAINT PK_FileNode PRIMARY KEY) AS NODE

GO

CREATE TABLE FileNodeArchiveEdge AS EDGE

GO

CREATE INDEX [IX_FileNodeArchiveEdge_ChildFile] ON [dbo].[FileNodeArchiveEdge] ($from_id)

GO

CREATE INDEX [IX_FileNodeArchiveEdge_ParentFile] ON [dbo].[FileNodeArchiveEdge] ($to_id)

GO
Run Code Online (Sandbox Code Playgroud)

填充演示数据库表:

INSERT INTO [FileNode] (ID) VALUES
            (1),(2),(3),(4),(5),
            (6),(7),(8),(9),(10),
            (11),(12),(13),(14),(15)

-- Convenient intermediate table
DECLARE @bridge TABLE (f BIGINT, t BIGINT)
INSERT INTO @bridge (f, t) VALUES
    (1,4),
    (4,9),
    (4,10),
    (1,5),
    (5,11),
    (11,12),
    (2,5),
    (2,6),
    (6,13),
    (6,14),
    (13,15),
    (14,15),
    (15,12),
    (7,14),
    (3,7),
    (3,8)

INSERT INTO FileNodeArchiveEdge
($from_id, $to_id)
SELECT 
    (SELECT $node_id FROM FileNode WHERE ID = f),
    (SELECT $node_id FROM FileNode WHERE ID = t)
FROM @bridge
Run Code Online (Sandbox Code Playgroud)

要获取文件的所有子 ID(重复相同的查询将占用内存资源并导致“USERSTORE_SCHEMAMGR”无法控制地增长):

DECLARE @parentId BIGINT = 1
SELECT 
    LAST_VALUE(f2.ID) WITHIN GROUP (GRAPH PATH)
FROM
    FileNode f1,
    FileNodeArchiveEdge FOR PATH contains_file,
    FileNode FOR PATH f2
WHERE
    f1.ID = @parentId
    AND MATCH(SHORTEST_PATH(f1(-(contains_file)->f2)+))
Run Code Online (Sandbox Code Playgroud)

重新运行提供的查询以检索特定文件的所有子节点,最终将看到“USERSTORE_SCHEMAMGR”内存管理员类型占用了大量已用资源。tempdb 也会不受控制地增长。

演示数据库查询太小,无法触发报告“内部”资源池的显式消息。但是,在同一台服务器上运行更大的查询应该会触发警告,并且性能仍然会受到影响。

以下查询可用于监控服务器性能:

-- Memory clerk usage
SELECT TOP(10) mc.[type] AS [Memory Clerk Type],
   CAST((SUM(mc.pages_kb)/1024.0) AS DECIMAL (15,2)) AS [Memory Usage (MB)]
FROM sys.dm_os_memory_clerks AS mc WITH (NOLOCK)
GROUP BY mc.[type]
ORDER BY SUM(mc.pages_kb) DESC OPTION (RECOMPILE);

/*
Example output of above query:

Memory Clerk Type                                            Memory Usage (MB)
------------------------------------------------------------ -----------------
USERSTORE_SCHEMAMGR                                                    9224.26
MEMORYCLERK_SQLSTORENG                                                 1114.73
MEMORYCLERK_SQLBUFFERPOOL                                               471.50
CACHESTORE_SEHOBTCOLUMNATTRIBUTE                                        376.47
MEMORYCLERK_SOSNODE                                                     292.02
MEMORYCLERK_SQLGENERAL                                                   19.84
MEMORYCLERK_SQLCLR                                                       12.04
MEMORYCLERK_SQLQUERYPLAN                                                  2.99
MEMORYCLERK_SQLLOGPOOL                                                    2.61
MEMORYCLERK_SQLTRACE                                                      2.14

*/

SELECT cache_memory_kb/1024.0 AS [cache_memory_MB],compile_memory_kb/1024 AS compile_memory_MB, used_memory_kb/1024.0 AS [used_memory_MB] FROM sys.dm_resource_governor_resource_pools
Run Code Online (Sandbox Code Playgroud)

为了快速查询服务器并消耗资源,我使用了以下 BASH 循环:

l=1000 # Number of loops
# The loop will probably need to be run 2M times or so to start to see significant usage.

c=0 # Loop tracker

touch marker # Alternate emergency stop: Remove the marker file from another terminal session.
time while [ $c -lt $l ] && [ -f "marker" ]; do
    c="$((${c}+1))"
    echo ${c}/${l}
    # Notes: SQLCMDPASSWORD has been set in environment variable
    #        child-query.sql contains the above child query to loop for the children of file ID 1.
    time sqlcmd -U db_user -S localhost -d DemoDatabase -i child-query.sql > /dev/null || break
done
rm marker
Run Code Online (Sandbox Code Playgroud)

DROPCLEANBUFFERS/FREEPROCCACHE/FLUSHPROCINDB DBCC 命令成功完成,但似乎没有效果。

是否有可以解决此问题的配置或程序,或者这是一个基本的服务器问题?

Arv*_*dar 5

我是 Microsoft SQL 团队的一名项目经理,负责监督 SQL Graph 功能。感谢您提供的详细信息,并对问题表示诚挚的歉意。此问题已上报给我们的工程团队,并正在优先调查。我们会及时通知您调查结果。

2021 年 8 月 4 日更新:此问题已在SQL Server 2019 CU12 中修复。

PS 同时,作为参考,这里有一些提示,说明您还可以如何对引擎盖下发生的事情进行进一步的自我调查(如果您愿意)。首先,SQL Server 中的任何内存分配通常都可以使用扩展事件 (XE) 机制详细跟踪到调用堆栈级别。第一步是定义一个 XE 会话,过滤特定的内存管理员名称,在本例中为USERSTORE_SCHEMAMGR

CREATE EVENT SESSION [TraceMemObj] ON SERVER 
ADD EVENT sqlos.page_allocated(
    ACTION(package0.callstack)
    WHERE (memory_clerk_name = 'USERSTORE_SCHEMAMGR')
    )
,
ADD EVENT sqlos.page_freed(
    ACTION(package0.callstack)
    WHERE (memory_clerk_name = 'USERSTORE_SCHEMAMGR')
    )
ADD TARGET package0.histogram  
(SET source_type=1,
     source=N'package0.callstack')
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_CPU,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF)
Run Code Online (Sandbox Code Playgroud)

启动扩展事件会话:

ALTER EVENT SESSION [TraceMemObj] on server state = start
Run Code Online (Sandbox Code Playgroud)

然后,等待问题重现,然后运行以下查询。单击查询的 XML 输出。

SELECT event_session_address,
       target_name,
       execution_count,
       CAST (target_data AS XML) AS MemObjData
FROM   sys.dm_xe_session_targets AS xst
       INNER JOIN
       sys.dm_xe_sessions AS xs
       ON (xst.event_session_address = xs.address)
WHERE  xs.name = 'TraceMemObj';
Run Code Online (Sandbox Code Playgroud)

使用SQLCallStackResolver 可以将上述原始调用堆栈解析为更具可读性的版本。可读的调用堆栈将使您了解大部分分配的来源。

确保在问题重现一两分钟后停止 XE 会话:

ALTER EVENT SESSION [TraceMemObj] ON SERVER 
STATE = STOP;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,对于您分享的再现场景,您会注意到顶部分配相关调用堆栈上的一些函数,这似乎表明与 SQL Graph 查询执行相关。