Was ist der Zweck dieser Uniq1002-Spalte in diesem Index-Scan?

7

Nehmen Sie den folgenden Repro:

USE tempdb;

IF OBJECT_ID(N'dbo.t', N'U') IS NOT NULL
DROP TABLE dbo.t
GO
CREATE TABLE dbo.t
(
    id int NOT NULL 
        PRIMARY KEY 
        NONCLUSTERED 
        IDENTITY(1,1)
    , col1 datetime NOT NULL
    , col2 varchar(800) NOT NULL
    , col3 tinyint NULL
    , col4 sysname NULL
);

INSERT INTO dbo.t (
      col1
    , col2
    , col3
    , col4
    ) 
SELECT TOP(100000) 
      CONVERT(datetime, 
         DATEADD(DAY, CONVERT(int, CRYPT_GEN_RANDOM(1)), '2000-01-01 00:00:00'))
    , replicate('A', 800)
    , sc2.bitpos
    , CONVERT(sysname, CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26))
FROM sys.syscolumns sc
    CROSS JOIN sys.syscolumns sc2;

Hier füge ich einen Clustered-Index zu einer Reihe von Spalten hinzu, die nicht eindeutig sind, und einen typischen einspaltigen Nicht-Clustered-Index:

CREATE CLUSTERED INDEX t_cx 
ON dbo.t (col1, col2, col3);

CREATE INDEX t_c1 ON dbo.t(col4); 

Diese Abfrage zwingt SQL Server, nach dem Clustered-Index zu suchen. Bitte verzeihen Sie die Verwendung des Indexhinweises. Dies war der schnellste Weg, um den Repro zu erhalten:

SELECT id
    , col1
    , col2
    , col3
FROM dbo.t aad WITH (INDEX = t_c1)
WHERE col4 = N'JSB'
    AND col1 > N'2019-05-30 00:00:00';

Der tatsächliche Abfrageplan zeigt eine nicht vorhandene Spalte in der Ausgabeliste für den nicht gruppierten Index-Scan:

Geben Sie hier die Bildbeschreibung ein

Angeblich stellt dies den Uniqifier dar, der im nicht eindeutigen Clustered-Index verwendet wird. Ist das der Fall? Ist eine so benannte Spalte immer der Clustered Index Uniqifier?

Max Vernon
quelle

Antworten:

9

Angeblich stellt dies den Uniqifier dar, der im nicht eindeutigen Clustered-Index verwendet wird. Ist das der Fall?

Ja.

Was ist der Zweck dieser Uniq1002-Spalte in diesem Index-Scan?

Jede Zeile im nicht gruppierten Index muss genau einer Zeile in der Basistabelle zugeordnet sein , damit Lesezeichen-Lookups (RID oder Schlüssel) ordnungsgemäß funktionieren. Diese Zuordnung wird vom "Zeilenlokator" bereitgestellt.

Bei Heap-Tabellen ist der Zeilen-Locator die RID. Bei gruppierten Zeilenspeichertabellen ist dies der Clustering-Schlüssel ( ggf. einschließlich des Eindeutigers ).

Damit die Schlüsselsuche in Ihrem Plan funktioniert, muss sie Zugriff auf den Zeilenlokator haben. Dies schließt den Eindeutiger ein , sodass er vom nicht gruppierten Index-Scan ausgegeben werden muss.

Der Eindeutiger wird im Teil der Zeile mit variabler Länge gespeichert, sodass er nur bei Bedarf Speicherplatz beansprucht (dh wenn tatsächlich ein doppelter Schlüssel vorhanden ist).

Ist eine solche Spalte immer der Clustered-Index-Eindeutiger?

Ja. Die Eindeutigkeitsspalte wird immer benannt UniqXXXX. Der mit Heap-Tabellen verknüpfte Zeilen-Locator wird benannt BmkXXXX. Der Zeilenlokator für eine Spaltenspeichertabelle wird benannt ColStoreLocXXXX.


Beobachtung des Eindeutigers

In SQL Server-Versionen, die ein funktionales query_trace_column_valueserweitertes Ereignis enthalten, können die Werte des Eindeutigers direkt beobachtet werden .

Dieses undokumentierte und nicht unterstützte Ereignis befindet sich im Debug- Kanal. Es wurde mit SQL Server 2016 eingeführt und funktioniert nicht mehr mit CU11 von SQL Server 2017.

Zum Beispiel:

CREATE TABLE #T (c1 integer NULL INDEX ic1 CLUSTERED, c2 integer NULL INDEX ic2 UNIQUE, c3 integer NULL);
GO
INSERT #T
    (c1, c2, c3)
VALUES 
    (100, 101, 0),
    (100, 102, 1),
    (100, 103, 2);
GO
DBCC TRACEON (2486);
SET STATISTICS XML ON;
SELECT T.* FROM #T AS T WITH (INDEX(ic2));
SET STATISTICS XML OFF;
DBCC TRACEOFF (2486);
GO
DROP TABLE #T;

Hat den Plan:

Planen

Unter SQL Server 2016 wird eine Ereignisausgabe wie die folgende erzeugt:

Ereignisausgabe

Paul White 9
quelle
2

Damit SQL Server einen nicht eindeutigen Clustered-Index erstellen kann , wird der physischen Struktur des Clustered-Index eine ausgeblendete "Spalte" hinzugefügt. Diese versteckte Spalte wird als Uniqifier bezeichnet und bietet, wie der Name schon sagt, einen Mechanismus, um sicherzustellen, dass jede Zeile im Clustered-Index eindeutig ist.

Wenn Sie sehen, dass diese Spalte in einem Abfrageplan angezeigt wird, ist dies ein guter Indikator dafür, dass die Clustering-Schlüsselspalten nicht als eindeutig definiert wurden. Möglicherweise liegt das daran, dass die Kombination von Spalten bekanntermaßen nicht eindeutig ist. Es ist auch möglich, dass der Designer der Tabelle einfach vergessen hat, das UNIQUEQualifikationsmerkmal zur CREATE CLUSTERED INDEXAnweisung hinzuzufügen .

Wenn wir den obigen Repro mit einem eindeutigen Clustered-Index neu erstellen, wird die Uniq1002Spalte nicht mehr im Abfrageplan angezeigt:

USE tempdb;

IF OBJECT_ID(N'dbo.t', N'U') IS NOT NULL
DROP TABLE dbo.t
GO
CREATE TABLE dbo.t
(
    id int NOT NULL 
        PRIMARY KEY 
        NONCLUSTERED 
        IDENTITY(1,1)
    , col1 datetime NOT NULL
    , col2 varchar(800) NOT NULL
    , col3 int NULL
    , col4 sysname NULL
);

INSERT INTO dbo.t (
      col1
    , col2
    , col3
    , col4
    ) 
SELECT TOP(100000) 
      CONVERT(datetime, DATEADD(DAY, CONVERT(int, CRYPT_GEN_RANDOM(1)), '2000-01-01 00:00:00'))
    , replicate('A', 800)
    , CONVERT(int, CRYPT_GEN_RANDOM(4))
    , CONVERT(sysname, CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26))
FROM sys.syscolumns sc
    CROSS JOIN sys.syscolumns sc2;

Hier ist der EINZIGARTIGE Clustered-Index:

CREATE UNIQUE CLUSTERED INDEX t_cx 
ON dbo.t (col1, col2, col3);

CREATE INDEX t_c1 ON dbo.t(col4); 

Und die Frage:

SELECT id
    , col1
    , col2
    , col3
FROM dbo.t aad WITH (INDEX = t_c1)
WHERE col4 = N'JSB'
    AND col1 > N'2019-05-30 00:00:00';

Der Plan zeigt dies nun für die nicht gruppierten Index-Scan-Ausgabespalten:

Geben Sie hier die Bildbeschreibung ein

Wenn ein nicht eindeutiger Clustered-Index erstellt wird, wird der Uniqifier automatisch hinzugefügt. Der Uniqifier wird auch zu jedem nicht gruppierten Index hinzugefügt, obwohl Sie ihn nicht "sehen" können, indem Sie sich die Eigenschaften des Index ansehen oder den Index "skripten".

Der Uniqifier ist eine 4-Byte-Spalte, die eine Ganzzahl enthält, die hinter den Kulissen für jede in die Tabelle eingefügte Zeile automatisch erhöht wird. Für die erste eingefügte Zeile ist kein Uniqifier erforderlich. Nur Zeilen, die nach der ersten Zeile hinzugefügt wurden, haben den Uniqifier.

Max Vernon
quelle