在内存非常低的 QA 服务器上,我在 SQL Server 2016 中的新表上创建聚集列存储索引时遇到错误 8545:
错误 8645:等待内存资源执行查询时发生超时。重新运行查询。
我可以轻松地在我的本地机器上重现大量请求的内存授权。对于以下代码:
DROP TABLE IF EXISTS dbo.MY_FIRST_FACT_TABLE;
CREATE TABLE dbo.MY_FIRST_FACT_TABLE (
ID BIGINT NOT NULL,
COL1 BIGINT NULL,
COL2 BIGINT NULL,
COL3 BIGINT NULL,
COL4 BIGINT NULL,
COL5 BIGINT NULL,
COL6 BIGINT NULL,
COL7 BIGINT NULL,
COL8 BIGINT NULL,
COL9 BIGINT NULL,
COL10 BIGINT NULL,
STRING1 VARCHAR(100) NULL,
STRING2 VARCHAR(100) NULL,
STRING3 VARCHAR(100) NULL,
STRING4 VARCHAR(100) NULL,
STRING5 VARCHAR(100) NULL
);
CREATE CLUSTERED COLUMNSTORE INDEX MY_FIRST_CCI ON dbo.MY_FIRST_FACT_TABLE;
我收到大约 512 MB 内存的内存请求。实际执行计划中有excessive memory grant的警告:
即使查询使用 0 KB 的内存,它仍然可能超时,具体取决于服务器上的其他活动。为什么 SQL Server 需要那么多内存?我该怎么办?
在CCI 查询性能的文档中有关于此的提示:
该引用表明表中的行数对 DOP 很重要,但对内存授予无关紧要。所需的内存还取决于列数,因此让我们使用列数较少的表进行测试,以便更轻松地查看内存授予的预期差异。我正在使用这个表和索引定义:
以下是一些测试的结果:
对于 0 行,DOP 降级为 1,但除此之外,行数似乎与内存授予无关。这无疑是一种严重的过度简化,但 SQL Server 可能会为每个 DOP 一次构建 CCI 一个行组,并在其进行时维护字符串字典。这在实践中听起来不错,意味着内存授予不会随表中的行数缩放,但如果不检查非常低的行数,SQL Server 可能会请求过多的内存授予。在大多数情况下,过多的内存授予无关紧要,因为
CREATE INDEX
速度太快了,但如果 SQL Server 等待内存授予,则可能会很重要。所以现在我知道为什么内存授予如此之大,但是可以做些什么呢?SQL Server 2014 引入了将索引定义定义为表定义的一部分的语法。可以编写一条同时创建表和索引的语句:
对于原始表,语法如下所示:
CREATE TABLE
语句不会生成实际计划,因此我无法立即判断这是否减少了内存授予。我在敲打 DMV 时尝试创建表格 1000 次,sys.dm_exec_query_memory_grants
但从未得到任何结果。这表明记忆没有被授予,但不是证据。我还尝试使用 query_memory_grant_usage 扩展事件,但没有得到单个CREATE TABLE
语句的任何结果。当然,最好的测试方法是在可用内存有限的情况下创建 CCI 原始工作流条件。我这样做了,没有遇到任何内存超时。
我想可以说在
CREATE TABLE
新表的语句中定义 CCI 是最佳实践。但是,如果您的工作负载导致此错误,那么更改CREATE TABLE
脚本可能不足以解决您将遇到的其他问题,例如开始将数据加载到 CCI 时的内存问题。