sql-server – 为什么创建一个简单的CCI行组最多需要30秒?

前端之家收集整理的这篇文章主要介绍了sql-server – 为什么创建一个简单的CCI行组最多需要30秒?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
当我注意到我的一些插件花费的时间比预期的要长时,我正在制作一个涉及CCI的演示.要重现的表定义:
DROP TABLE IF EXISTS dbo.STG_1048576;
CREATE TABLE dbo.STG_1048576 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_1048576
SELECT TOP (1048576) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL,INDEX CCI CLUSTERED COLUMNSTORE);

对于测试,我将从登台表中插入所有1048576行.这足以填充一个压缩的行组,只要它不会因某种原因被修剪.

如果我插入所有整数mod 17000,则需要不到一秒钟:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 17000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

sql Server Execution Times: cpu time = 359 ms,elapsed time = 364 ms.

但是,如果我插入相同的整数mod 16000,它有时需要超过30秒:

TRUNCATE TABLE dbo.CCI_BIGINT;

INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_1048576
OPTION (MAXDOP 1);

sql Server Execution Times: cpu time = 32062 ms,elapsed time = 32511 ms.

这是一项可在多台机器上完成的可重复测试.当mod值改变时,似乎在经过的时间内有一个清晰的模式:

MOD_NUM TIME_IN_MS
1000    2036
2000    3857
3000    5463
4000    6930
5000    8414
6000    10270
7000    12350
8000    13936
9000    17470
10000   19946
11000   21373
12000   24950
13000   28677
14000   31030
15000   34040
16000   37000
17000   563
18000   583
19000   576
20000   584

如果你想自己运行测试,可以随意修改我写的测试代码here.

对于mod 16000插入,我在sys.dm_os_wait_stats中找不到任何有趣的内容

╔════════════════════════════════════╦══════════════╗
║             wait_type              ║ diff_wait_ms ║
╠════════════════════════════════════╬══════════════╣
║ XE_DISPATCHER_WAIT                 ║       164406 ║
║ QDS_PERSIST_TASK_MAIN_LOOP_SLEEP   ║       120002 ║
║ LAZYWRITER_SLEEP                   ║        97718 ║
║ LOGMGR_QUEUE                       ║        97298 ║
║ DIRTY_PAGE_POLL                    ║        97254 ║
║ HADR_FILESTREAM_IOMGR_IOCOMPLETION ║        97111 ║
║ sqlTRACE_INCREMENTAL_FLUSH_SLEEP   ║        96008 ║
║ REQUEST_FOR_DEADLOCK_SEARCH        ║        95001 ║
║ XE_TIMER_EVENT                     ║        94689 ║
║ SLEEP_TASK                         ║        48308 ║
║ BROKER_TO_FLUSH                    ║        48264 ║
║ CHECKPOINT_QUEUE                   ║        35589 ║
║ SOS_SCHEDULER_YIELD                ║           13 ║
╚════════════════════════════════════╩══════════════╝

为什么ID%16000的插入时间比ID%17000的插入时间长得多?

解决方法

在许多方面,这是预期的行为.根据输入数据分布,任何一组压缩例程都将具有广泛的性能.我们希望交换数据加载速度以获得存储大小和运行时查询性能.

由于VertiPaq是一项专有实施,因此对于您将要获得的答案的详细程度有一个明确的限制,细节是一个严密保密的秘密.即便如此,我们知道VertiPaq包含以下例程:

>值编码(缩放和/或转换值以适合少量位)
>字典编码(对唯一值的整数引用)
>运行长度编码(将重复值的运行存储为[值,计数]对)
>比特打包(将流存储在尽可能少的位)

通常,数据将是值或字典编码,然后将应用RLE或比特打包(或者在段数据的不同子部分上使用的RLE和比特打包的混合).决定应用哪种技术的过程可涉及生成直方图以帮助确定如何实现最大比特节省.

使用Windows性能分析器捕获慢速案例并使用Windows性能分析器分析结果,我们可以看到绝大部分执行时间都用于查看数据的聚类,构建直方图以及决定如何对其进行最佳分区储蓄:

对于在段中出现至少64次的值,进行最昂贵的处理.这是一种启发式方法,可以确定纯RLE何时可能是有益的.更快的情况导致不纯的存储,例如比特打包表示,具有更大的最终存储大小.在混合情况下,具有64次或更多次重复的值是RLE编码的,其余的是比特打包的.

当具有64次重复的不同值的最大数量出现在最大可能段中时发生最长持续时间,即1,048,576行,其中16,384组值各自具有64个条目.对代码的检查揭示了昂贵处理的硬编码时间限制.这可以在其他VertiPaq实现中配置,例如SSAS,但据我所知,不在sql Server中.

可以使用undocumented DBCC CSINDEX command获取对最终存储布置的一些了解.这显示了RLE头和数组条目,RLE数据中的任何书签,以及位包数据的简要摘要(如果有的话).

有关更多信息,请参阅:

> The VertiPaq Engine in DAX,Alberto Ferrari和Marco Russo
> Microsoft Patent WO2015038442:使用DBMS引擎处理数据集
> Microsoft Patent WO2010039898:高效的大规模过滤和/或排序,用于查询基于列的数据编码结构

原文链接:https://www.f2er.com/mssql/79330.html

猜你在找的MsSQL相关文章