Ran*_*gen 11 sql-server sql-server-2017 sparse-column
在对稀疏列进行一些测试时,正如您所做的那样,出现了性能下降,我想知道其直接原因。
数据线
我创建了两个相同的表,一个有 4 个稀疏列,一个没有稀疏列。
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
Run Code Online (Sandbox Code Playgroud)
数据管理语言
然后我在两者中插入了大约2540 个非空值。
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Run Code Online (Sandbox Code Playgroud)
之后,我在两个表中插入了1M 个 NULL值
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Run Code Online (Sandbox Code Playgroud)
查询
非稀疏表执行
在新创建的非稀疏表上运行此查询两次时:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
逻辑读取显示5257页
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)
并且 CPU 时间为343 毫秒
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
Run Code Online (Sandbox Code Playgroud)
稀疏表执行
在稀疏表上运行相同的查询两次:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
读数较低,1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)
但是 cpu 时间更高,547 ms。
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Run Code Online (Sandbox Code Playgroud)
问题
原始问题
由于NULL值不直接存储在稀疏列中,cpu 时间的增加是否是由于将NULL值作为结果集返回?或者它只是文档中指出的行为?
稀疏列减少了空值的空间需求,但代价是检索非空值的开销更大
还是开销仅与使用的读取和存储有关?
即使在执行选项后使用丢弃结果运行 ssms 时,与非稀疏选择(219 毫秒)相比,稀疏选择的 CPU 时间(407 毫秒)也更高。
编辑
这可能是非空值的开销,即使只有 2540 个,但我仍然不相信。
这似乎是相同的性能,但稀疏因素丢失了。
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Run Code Online (Sandbox Code Playgroud)
似乎有大约相同的执行时间:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
Run Code Online (Sandbox Code Playgroud)
但为什么现在逻辑读取量相同?除了包含的 ID 字段和其他一些非数据页面之外,稀疏列的过滤索引不应该存储任何内容吗?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
Run Code Online (Sandbox Code Playgroud)
以及两个指数的大小:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Run Code Online (Sandbox Code Playgroud)
为什么它们的大小相同?稀疏性丢失了吗?
额外信息
select @@version
Run Code Online (Sandbox Code Playgroud)
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) - 14.0.3223.3 (X64) 2019 年 7 月 12 日 17:43:08 版权所有 (C) 2017 Microsoft Corporation Developer Edition(64 位),Windows Server 2012 R2 Datacenter 63。 9600:)(管理程序)
在运行查询并仅选择ID字段时,cpu 时间相当,稀疏表的逻辑读取较少。
桌子的大小
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
Run Code Online (Sandbox Code Playgroud)
强制使用聚集索引或非聚集索引时,cpu 时间差异仍然存在。
或者它只是文档中指出的行为?
似乎是这样。文档中提到的“开销”似乎是 CPU 开销。
分析这两个查询,稀疏查询采样了 367 毫秒的 CPU,而非稀疏查询采样了 284 毫秒的 CPU。这是 83 毫秒的差异。
大部分在哪里?
两个配置文件看起来非常相似,直到它们到达sqlmin!IndexDataSetSession::GetNextRowValuesInternal. 在这一点上,稀疏代码沿着运行的路径向下移动sqlmin!IndexDataSetSession::GetDataLong,它调用一些看起来与稀疏列特征 ( HasSparseVector, StoreColumnValue)相关的函数,并且加起来为 (42 + 11 =) 53 毫秒。
为什么它们的大小相同?稀疏性丢失了吗?
是的,当稀疏列用作索引键时,稀疏存储优化似乎不会延续到非聚集索引。因此,无论稀疏性如何,非聚集索引键列都会占用其完整大小,但如果包含的列稀疏且为 NULL,则它们占用的空间为零。
查看DBCC PAGE带有 NULL 值稀疏列的聚集索引页的输出,我可以看到记录长度为 11(ID 为 4 + 7 为标准的每条记录开销):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
Run Code Online (Sandbox Code Playgroud)
对于过滤索引,记录始终为 40,即所有键列大小的总和(4 字节 ID + 20 字节 charval + 4 字节 varcharval + 4 字节 intval + 8 字节 big intval = 40 字节)。
出于某种原因,DBCC PAGE不包括索引记录的“记录大小”中的 7 字节开销:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
Run Code Online (Sandbox Code Playgroud)
未过滤的索引大小较小(4 字节 ID + 4 字节 intval + 4 字节 varcharval = 12 字节),因为包含两个稀疏列的列,这再次得到了稀疏优化:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
Run Code Online (Sandbox Code Playgroud)
我想这种行为差异与文档页面中列出的限制之一一致:
稀疏列不能是聚集索引或唯一主键索引的一部分
它们被允许作为非聚集索引中的键,但它们不是存储的,呃,稀疏的。
| 归档时间: |
|
| 查看次数: |
277 次 |
| 最近记录: |