稀疏列,CPU时间和过滤索引


10

稀疏

当像您一样对稀疏列进行一些测试时,我想知道造成性能下降的直接原因。

DDL

我创建了两个相同的表,一个表具有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);

DML

然后,我在两者中都插入了约2540个NON-NULL值。

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;

之后,我在两个表中都插入了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;

查询

非稀疏表执行

在新创建的非稀疏表上运行此查询两次时:

SET STATISTICS IO, TIME ON;
SELECT  * FROM dbo.nonsparse
WHERE   1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);

逻辑读取显示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.

而CPU时间是343毫秒

 SQL Server Execution Times:
   CPU time = 343 ms,  elapsed time = 3850 ms.

稀疏表执行

在稀疏表上两次运行相同的查询:

SELECT  * FROM dbo.sparse
WHERE   1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);

读数较低,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.

但是cpu时间更长,为547 ms

 SQL Server Execution Times:
   CPU time = 547 ms,  elapsed time = 2406 ms.

稀疏表执行计划

非稀疏表执行计划


问题

原始问题

由于NULL值未直接存储在稀疏列中,因此cpu时间的增加是否可能是由于将NULL值作为结果集返回?还是仅仅是文档中指出的行为?

稀疏列减少了空值的空间要求,但代价是检索非空值的更多开销

还是仅与读取和存储相关的间接费用?

即使运行带有执行后丢弃结果选项的ssms,与非稀疏(219 ms)相比,稀疏选择的cpu时间也较高(407 ms)。

编辑

即使只有2540,也可能是非null值的开销,但是我仍然不相信。

这似乎具有大约相同的性能,但是稀疏因子丢失了。

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);

似乎具有相同的执行时间:

 SQL Server Execution Times:
   CPU time = 297 ms,  elapsed time = 292 ms.

 SQL Server Execution Times:
   CPU time = 281 ms,  elapsed time = 319 ms.

但是,为什么逻辑现在读取相同的数量?稀疏列的过滤索引是否不应该存储除了包含的ID字段和其他一些非数据页面以外的任何内容?

Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785

以及两个索引的大小:

RowCounts   Used_MB Unused_MB   Total_MB
1000000     45.20   0.06        45.26

为什么这些尺寸相同?稀疏消失了吗?

使用过滤索引时的两个查询计划


额外信息

select @@version

Microsoft SQL Server 2017(RTM-CU16)(KB4508218)-14.0.3223.3(X64)2019年7月12日17:43:08版权所有(C)2017 Windows Server 2012 R2 Datacenter 6.3(Build)上的Microsoft Corporation Developer Edition(64位) 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

当强制使用聚集索引或非聚集索引时,cpu时间差仍然存在。


1
您能得到编辑后查询的计划吗?
George.Palacios

1
@ George.Palacios添加了它们:)
Randi Vertongen

Answers:


6

还是仅仅是文档中提到的行为?

似乎如此。文档中提到的“开销”似乎是CPU开销。

对这两个查询进行概要分析后,稀疏查询采样了367 ms的CPU,而非稀疏查询则采样了284 ms的CPU。相差83毫秒。

Perfview的屏幕截图显示了运行查询的线程的总CPU

大部分在哪里?

直到到达为止,两个配置文件看起来都非常相似sqlmin!IndexDataSetSession::GetNextRowValuesInternal。那时,稀疏代码沿着一条运行路径运行sqlmin!IndexDataSetSession::GetDataLong,该路径调用一些看起来与稀疏列特征(HasSparseVectorStoreColumnValue)相关的函数,加起来(42 + 11 =)53 ms。

稀疏列的CPU差异屏幕截图

为什么这些尺寸相同?稀疏消失了吗?

是的,当稀疏列用作索引键时,稀疏存储优化似乎不会延续到非聚集索引。因此,非聚集索引键列无论其稀疏度如何都占用其全部大小,但是如果包含的列稀疏且为NULL,则它们所占的空间为零。

查看DBCC PAGE具有NULL值稀疏列的聚簇索引页的输出,我可以看到记录长度为11(ID为4,每个记录的标准开销为7):

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP    Record Size = 11

对于过滤后的索引,记录始终为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

未过滤的索引大小较小(4个字节的ID + 4个字节的intval + 4个字节的varcharval = 12个字节),因为其中包括两个稀疏列,它们再次获得了稀疏性优化:

Record Type = INDEX_RECORD          Record Attributes =  NULL_BITMAP    Record Size = 12

我猜想这种行为上的差异与docs页面中列出的限制之一有关:

稀疏列不能是聚集索引或唯一主键索引的一部分

可以将它们用作非聚集索引中的键,但是不会稀疏地存储它们。


2
好一个!再次感谢!
Randi Vertongen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.