Sparsing
При выполнении некоторых тестов для разреженных столбцов, как и у вас, произошел спад производительности, который я хотел бы узнать по прямой причине.
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 значений 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.
И время процессора составляет 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.
Но время процессора выше, 547 мс .
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
План выполнения разреженных таблиц
план разбора не разреженных таблиц
Вопросов
Оригинальный вопрос
Поскольку значения NULL не сохраняются непосредственно в разреженных столбцах, может ли увеличение времени процессора быть связано с возвратом значений NULL в качестве набора результатов? Или это просто поведение, указанное в документации ?
Разреженные столбцы уменьшают требования к пространству для нулевых значений за счет дополнительных затрат на извлечение ненулевых значений
Или накладные расходы связаны только с чтением и хранением?
Даже при запуске ssms с опциями отмены после выполнения время процессора разреженного выбора было выше (407 мс) по сравнению с непрореженным (219 мс).
РЕДАКТИРОВАТЬ
Возможно, это были издержки ненулевых значений, даже если присутствуют только 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);
Кажется, примерно одинаковое время выполнения:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
Но почему логические чтения сейчас одинаковы? Разве в отфильтрованном индексе для разреженного столбца не должно храниться ничего, кроме включенного поля идентификатора и некоторых других страниц без данных?
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) 12 июля 2019 г. 17:43:08 Авторское право (C) 2017 Выпуск Microsoft Corporation для разработчиков (64-разрядная версия) в Windows Server 2012 R2 Datacenter 6.3 (сборка) 9600 :) (гипервизор)
При выполнении запросов и выборе только поля идентификатора время процессора сравнимо с меньшим логическим чтением для разреженной таблицы.
Размер столов
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
При принудительном использовании кластеризованного или некластеризованного индекса разница во времени процессора сохраняется.
источник
Ответы:
Кажется, так. Упомянутые в документации «накладные расходы», по-видимому, являются накладными расходами ЦП.
Профилируя два запроса, разреженный запрос выбрал 367 мс ЦП, в то время как у не разреженного было 284 мс ЦП. Это разница 83 мс.
Где это больше всего?
Оба профиля выглядят очень похожими, пока не дойдут до
sqlmin!IndexDataSetSession::GetNextRowValuesInternal
. В этот момент разреженный код идет по пути, который выполняетсяsqlmin!IndexDataSetSession::GetDataLong
, который вызывает некоторые функции, которые выглядят так, как будто они связаны с функцией разреженного столбца (HasSparseVector
,StoreColumnValue
), и в сумме составляют (42 + 11 =) 53 мс.Да, похоже, что оптимизация разреженного хранилища не переносится на некластеризованные индексы, когда разреженный столбец используется в качестве ключа индекса. Таким образом, столбцы ключа кластеризованного индекса занимают свой полный размер независимо от разреженности, но включенные столбцы занимают нулевое пространство, если они разрежены и имеют значение NULL.
Глядя на
DBCC PAGE
выходные данные страницы кластерного индекса с разреженными столбцами, имеющими значение NULL, я вижу, что длина записи равна 11 (4 для идентификатора + 7 для стандартных накладных расходов на запись):Для отфильтрованного индекса запись всегда равна 40, что является суммой размера всех ключевых столбцов (4-байтовый идентификатор + 20-байтовый charval + 4-байтовый varcharval + 4-байтовый intval + 8-байтовый большой intval = 40 байт).
По некоторым причинам
DBCC PAGE
не включает 7-байтовые издержки в «Размер записи» для записей индекса:Размер нефильтрованного индекса меньше (4 байта ID + 4 байта intval + 4 байта varcharval = 12 байтов), поскольку два разреженных столбца включают столбцы, что опять-таки позволяет оптимизировать разреженность:
Я предполагаю, что эта разница в поведении соответствует одному из ограничений, перечисленных на странице документации:
Им разрешено быть ключами в некластеризованных индексах, но они не хранятся редко.
источник