Рідкі стовпці, процесорний час та відфільтровані індекси


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;

Потім я вставив значення NM 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 мс).

EDIT

Можливо, це було б накладне значення ненульових значень, навіть якщо їх є лише 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 (Build) 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

При вимушенні або кластерного, або некластеризованого індексу різниця у процесорі залишається.


1
Чи можете ви отримати плани щодо запиту після редагування?
George.Palacios

1
@ George.Palacios додав їх :)
Randi

Відповіді:


6

Або це просто поведінка, як зазначено в документації?

Здається, так. Згадані в документації «накладні витрати», схоже, є накладними процесорами.

Профілюючи два запити, рідкісний запит відбирав 367 мс процесора, тоді як непростий мав 284 мс процесора. Це різниця у 83 мс.

скріншот від Perfview, який показує загальний процесор для потоку, який виконував запит

Де це найбільше?

Обидва профілі виглядають дуже схоже, поки не потрапляють sqlmin!IndexDataSetSession::GetNextRowValuesInternal. У цей момент розріджений код йде вниз шляхом, який проходить sqlmin!IndexDataSetSession::GetDataLong, який викликає деякі функції, схожі на те, що вони відносяться до функції розрідженого стовпця ( HasSparseVector, StoreColumnValue), і дорівнює (42 + 11 =) 53 мс.

скріншот різниці процесорів для розрідженої колонки

Чому вони однакового розміру? Чи була втрачена розрідженість?

Так, виявляється, що розріджена оптимізація зберігання не переходить до некластеризованих індексів, коли рідкий стовпець використовується як індексний ключ. Таким чином, некластеризовані стовпці ключових індексів займають їх повний розмір незалежно від розрідженості, але включені стовпці займають нульовий простір, якщо вони рідкі та NULL.

Дивлячись на DBCC PAGEвихід з кластерної індексної сторінки з розрізненими стовпцями NULL, я бачу, що довжина запису становить 11 (4 для ID + 7 для стандартних накладних витрат на запис):

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

Для відфільтрованого індексу запис завжди дорівнює 40, що є сумою розміру всіх ключових стовпців (4 байти ID + 20 байт-шарвал + 4 байт варчарвал + 4 байти intval + 8 байт великий 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

Я думаю, що ця різниця в поведінці відповідає одному з обмежень, перелічених на сторінці документів:

Рідкий стовпець не може бути частиною кластерного індексу або унікального індексу первинного ключа

Їм дозволяється бути ключами в некластеризованих індексах, але вони не зберігаються, ну, рідко.


2
Хороший! Знову дякую!
Ранді Вертонген
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.