kir*_*ner 6 sql-server columnstore sql-server-2017
我有一个带有聚集列存储索引的简单表:
ID INT NOT NULL,
Hash BINARY(20) NOT NULL
Run Code Online (Sandbox Code Playgroud)
此表有一些十亿行和根据sp_spaceused
,sys.allocation_units
并SSMS报告,其大小约为25GB。
我的问题是我无法解释所有这些空间。查询sys.column_store_row_groups
并且sys.column_store_segments
只给我大约 7,8GB。索引不使用字典:primary_dictionary_id
并且secondary_dictionary_id
对于所有段都是 -1。查询sys.column_store_dictionaries
根本不返回任何行。
元组移动器已完成其工作,所有行组都处于压缩状态。我已经试过了ALTER INDEX REORGANIZE
以防万一。
我对大小差异的唯一想法是一些我没有考虑的类似字典的东西。关于我可能缺少什么的任何想法?
我正在运行 SQL Server 2017 (RTM-CU4)。
编辑 1:
这是 sp_spaceused 用于相关表的输出:
+--------+------------+-------------+-------------+------------+----------+
| name | rows | reserved | data | index_size | unused |
+--------+------------+-------------+-------------+------------+----------+
| IdsBin | 1073741824 | 25028112 KB | 25007432 KB | 16 KB | 20664 KB |
+--------+------------+-------------+-------------+------------+----------+
Run Code Online (Sandbox Code Playgroud)
编辑2:
这是一个包含 100 万行的复制脚本。它在我的机器上运行大约 1 分钟。警告:它会删除并重新创建一个新数据库
USE master;
GO
DROP DATABASE IF EXISTS MyDbWeirdTest;
GO
CREATE DATABASE MyDbWeirdTest;
GO
USE MyDbWeirdTest;
GO
CREATE TABLE IdsBin (
ID INT NOT NULL,
Hash BINARY(20) NOT NULL
);
CREATE CLUSTERED COLUMNSTORE INDEX ix1 ON IdsBin
GO
CREATE TYPE tBin AS TABLE (
ID INT,
Hash BINARY(20)
);
GO
CREATE OR ALTER PROCEDURE pBin (
@ids AS dbo.tBin READONLY
)
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.IdsBin
SELECT ID, Hash FROM @ids;
END;
GO
SET NOCOUNT ON;
DECLARE @i INT = 1, @t INT = 1;
DECLARE @tvp dbo.tBin;
WHILE @t <= 1000000
BEGIN
DELETE @tvp;
BEGIN TRAN;
WHILE @i <= 1000
BEGIN
INSERT @tvp VALUES (@t, HASHBYTES('SHA1', CAST(@t AS BINARY(4))));
SET @i = @i + 1;
SET @t = @t + 1;
END;
EXEC pBin @tvp;
COMMIT;
SET @i = 1;
END;
GO
ALTER INDEX ix1 on IdsBin REBUILD;
GO
Run Code Online (Sandbox Code Playgroud)
对于此重现,sp_spaceused 显示:
+--------+----------------------+----------+----------+------------+--------+
| name | rows | reserved | data | index_size | unused |
+--------+----------------------+----------+----------+------------+--------+
| IdsBin | 1000000 | 22728 KB | 22640 KB | 0 KB | 88 KB |
+--------+----------------------+----------+----------+------------+--------+
Run Code Online (Sandbox Code Playgroud)
sys.column_store_row_groups:
+-----------+----------+------------------+--------------+---------------------+-------+-------------------+------------+--------------+---------------+
| object_id | index_id | partition_number | row_group_id | delta_store_hobt_id | state | state_description | total_rows | deleted_rows | size_in_bytes |
+-----------+----------+------------------+--------------+---------------------+-------+-------------------+------------+--------------+---------------+
| 901578250 | 1 | 1 | 0 | NULL | 3 | COMPRESSED | 1000000 | 0 | 5896938 |
+-----------+----------+------------------+--------------+---------------------+-------+-------------------+------------+--------------+---------------+
Run Code Online (Sandbox Code Playgroud)
所以 sp_spaceused 给了我大约 22MB 并且 sys.allocation_units(未显示)同意。但似乎没有列存储 DMV 同意这个数字,他们说索引的大小小于 6MB。
sys.column_store_segments
并sys.column_store_row_groups
存储一些有关 ColumnStore 数据的元数据信息,但我相信最终代表压缩的大小。还分配了 LOB 结构,您可以在分配单元/分区 DMV 中看到未压缩的大小(如果您以某种方式访问页面DBCC PAGE
,我敢打赌您会看到它们相对空)。换句话说,sys.column_store_row_groups
显示了这些页面上存储了多少数据,但不会添加这些页面上的可用空间(它们仍然占用数据文件和内存中的空间,例如碎片索引或带有填充因子非常低)。
我运行了你的重现,这是我所看到的:
SELECT
a.[type_desc],
p.[rows],
a.total_pages, reserved_kb = a.total_pages * 8,
a.used_pages, data_kb = a.used_pages * 8
FROM sys.allocation_units AS a
INNER JOIN sys.partitions AS p
ON a.container_id = p.[partition_id]
WHERE p.[object_id] = OBJECT_ID(N'dbo.IdsBin');
Run Code Online (Sandbox Code Playgroud)
结果:
除了未使用(我没有费心计算)之外,结果与以下内容相符sp_spaceused
:
您也可以在以下位置确认这些数字sys.dm_db_partition_stats
:
SELECT
lob_reserved_page_count, reserved_kb = lob_reserved_page_count * 8,
lob_used_page_count, data_kb = lob_used_page_count * 8
FROM sys.dm_db_partition_stats
WHERE [object_id] = OBJECT_ID(N'dbo.IdsBin');
Run Code Online (Sandbox Code Playgroud)
结果:
您提到了这一点,但我只是想明确指出这一点sp_spaceused
并allocation_units
反映页数,无论任何页面有多满或多空。行组 DMV 仅反映实际数据。文件指出(强调我的):
该行组中所有数据的大小(以字节为单位)(不包括元数据或共享字典)
然而sys.dm_db_partition_stats
,例如,明确指出整个页面,而不是数据,尽管我认为他们应该在此处指定每个 LOB 是一个 8K 页面:
用于存储和管理分区中列存储索引的 LOB 总数。
您想信任哪个号码,这取决于您。
顺便说一句,Niko Neugebauer 谈到了列存储 DMV 中仅公开字典的压缩大小这一事实,并提出了有关此问题的反馈项。在我看来,列存储 DMV 中还可能公开其他信息。