聚集列存储索引空间使用情况

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_spaceusedsys.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。

Aar*_*and 3

sys.column_store_segmentssys.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)

结果:

AU/分区查询

除了未使用(我没有费心计算)之外,结果与以下内容相符sp_spaceused

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)

结果:

dm_db_partition_stats

您提到了这一点,但我只是想明确指出这一点sp_spaceusedallocation_units反映页数,无论任何页面有多满或多空。行组 DMV 仅反映实际数据。文件指出(强调我的):

该行组中所有数据的大小(以字节为单位)(不包括元数据或共享字典)

然而sys.dm_db_partition_stats,例如,明确指出整个页面,而不是数据,尽管我认为他们应该在此处指定每个 LOB 是一个 8K 页面:

用于存储和管理分区中列存储索引的 LOB 总数。

您想信任哪个号码,这取决于您。

顺便说一句,Niko Neugebauer 谈到了列存储 DMV 中仅公开字典的压缩大小这一事实并提出了有关此问题的反馈项。在我看来,列存储 DMV 中还可能公开其他信息。