AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 319909
Accepted
Joe Obbish
Joe Obbish
Asked: 2022-11-19 13:13:44 +0800 CST2022-11-19 13:13:44 +0800 CST 2022-11-19 13:13:44 +0800 CST

为什么在使用表变量时,一个简单的本机编译存储过程会耗尽内存?

  • 772

我的 SQL Server 版本是 SQL Server 2019 (RTM-CU18)。以下重现代码需要创建一个内存文件组。对于后续的任何人,请记住内存中的文件组一旦创建就不能从数据库中删除。

我有一个简单的内存表,我在其中插入 1 - 1200 之间的整数:

DROP TABLE IF EXISTS [dbo].[InMem];

CREATE TABLE [dbo].[InMem] (
    i [int] NOT NULL,
    CONSTRAINT [PK_InMem]  PRIMARY KEY NONCLUSTERED (i ASC)
) WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_ONLY );

INSERT INTO [dbo].[InMem]
SELECT TOP (1200) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

我还有以下本机编译的存储过程:

GO

CREATE OR ALTER PROCEDURE p1
WITH NATIVE_COMPILATION, SCHEMABINDING 
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
    SELECT c1.i, c2.i, c3.i
    FROM dbo.[InMem] c1
    CROSS JOIN dbo.[InMem] c2
    CROSS JOIN dbo.[InMem] c3
    WHERE c1.i + c2.i + c3.i = 3600;
END;

GO  

该过程在执行时返回一行。在我的机器上大约需要 32 秒才能完成。在执行时,我无法观察到内存使用方面的任何异常行为。

我可以创建一个类似的表类型:

CREATE TYPE [dbo].[InMemType] AS TABLE(
i [int] NOT NULL,
INDEX [ix_WordBitMap] NONCLUSTERED (i ASC)
) WITH ( MEMORY_OPTIMIZED = ON );

以及相同的存储过程,但改用表类型:

GO

CREATE OR ALTER PROCEDURE p2 (@t dbo.[InMemType] READONLY)
WITH NATIVE_COMPILATION, SCHEMABINDING 
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
    SELECT c1.i, c2.i, c3.i
    FROM @t c1
    CROSS JOIN @t c2
    CROSS JOIN @t c3
    WHERE c1.i + c2.i + c3.i = 3600;
END;

GO

新存储过程在大约一分钟后抛出错误:

消息 701,级别 17,状态 154,过程 p2,第 6 行 [批处理起始行 57] 资源池“默认”中的系统内存不足,无法运行此查询。

sys.dm_os_memory_clerks当程序执行时,我可以通过查询dmv看到 MEMORYCLERK_XTP 内存管理员使用的内存量增加到数据库的大约 2800 MB 。根据sys.dm_db_xtp_memory_consumersDMV,几乎所有的内存使用似乎都来自“64K 页面池”消费者:

在此处输入图像描述

在此处输入图像描述

作为参考,这里是我如何执行新存储过程的。它使用与表相同的 1200 行:

DECLARE @t dbo.[InMemType];

INSERT INTO @t (i)
SELECT i
from [dbo].[InMem];

EXEC p2 @t;

生成的查询计划是一个没有阻塞运算符的简单嵌套循环计划。根据请求,这是第二个存储过程的估计查询计划。

我不明白为什么当我使用表值参数时,这样的查询的内存使用量会增长到超过 2 GB。我已经阅读了各种文档和内存中 OLTP 白皮书,但找不到任何关于此行为的参考。

使用 ETW 跟踪,我可以看到第一个过程将其大部分 cpu 时间用于调用hkengine!HkCursorHeapGetNext,而第二个过程将其大部分 cpu 时间用于调用hkengine!HkCursorRangeGetNext. 我还可以获得这两个程序的 C 源代码。第一个程序在这里,第二个程序有内存问题,在这里。但是,我不知道如何阅读 C 代码,所以我不知道如何进一步调查。

为什么在对表值参数执行嵌套循环时,一个简单的本机编译存储过程会使用超过 2 GB 的内存?当我在存储过程之外运行查询时,也会出现此问题。

sql-server
  • 1 1 个回答
  • 646 Views

1 个回答

  • Voted
  1. Best Answer
    Paul White
    2022-11-20T06:41:01+08:002022-11-20T06:41:01+08:00

    当通过 Bw 树(范围)索引使用和访问表变量时,当引擎找到起始条目(hkengine!HkCursorRangeGetFirst和hkengine!BwFindFirst)时,会在每次扫描开始时分配内存。似乎没有维护排序的偏移数组,因此需要定位和排序第一页上的行(碰巧使用快速排序)。

    快速排序调用堆栈

    内存分配是使用 执行的hkengine!IncrAllocAlloc,它从一个块开始递增地工作。当需要一个新块时,hkengine!IoPagePool<65536>::AllocatePage调用它,这是您看到的 64K 分配的来源。

    64K分配调用栈

    在为堆游标找到第一行后,不会释放此内存。

    对于常规内存表,相应的内存分配是使用hkengine!varAllocAlloc从 varheap 分配的。hkengine!varAllocFree与表变量的情况相反,分配之后不久就会调用,释放内存。

    Bw-trees 最近发生了几次“内存泄漏”。例如,SQL Server 2019 CU 17中列出了两个:

    每当有并发插入时,具有非聚集索引的内存表上的“范围索引堆”下就会发生内存泄漏。

    并行索引扫描后,内存表的范围索引发生内存泄漏。

    在表变量情况下每次扫描开始时的内存分配不会像这样泄漏,但它们不会在表变量超出范围之前释放。

    表变量调用栈

    当嵌套循环重新启动时,测试查询中的第二个和第三个表的游标扫描开始多次。在您的情况下,累积的内存太多,并且在变量超出范围释放内存之前查询中止。

    SQL Server 2022 RTM 中的情况相同,只是sys.dm_db_xtp_memory_consumers不包括 64K 页面池。您仍然可以看到内存在增加sys.dm_os_memory_clerks。似乎 2022 安排能够消耗所有可用内存。我必须将缓冲池大小减少到 2.6GB 以下才能得到 OOM 错误。SQL Server 2019 使用 4GB 缓冲池引发错误。


    从 SQL Server 2016 开始,每个 hekaton 表都有自己的 varheap。除其他好处外,这意味着可以独立于任何索引扫描表。SQL Server 2014 没有 hekaton 表扫描的概念,因为行仅通过索引连接。表变量尚未更新为使用 varheap 方案,因此无法支持表扫描。

    游标代码可能已更新以反映新的 varheap 安排,但忽略了仍在用于表变量的原始实现。

    • 15

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve