Ich habe das gleiche Dichteproblem bei einigen der nicht gruppierten Indizes in den größten Datenbanken gesehen, auf die ich Zugriff habe. Zunächst beginne ich mit einigen Beobachtungen, die ich zu Histogrammen und Dichteberechnungen gemacht habe:
- SQL Server kann den Primärschlüssel in der Tabelle verwenden, um etwas über die Dichte beider Spalten abzuleiten. Dies bedeutet, dass die Dichte, die die PK-Spalten enthält, normalerweise sehr genau ist.
- Die Dichteberechnung für die erste Spalte in der Statistik stimmt mit dem Histogramm überein. Wenn das Histogramm die Daten nicht gut modelliert, ist die Dichte möglicherweise ausgeschaltet.
- Um das Histogramm zu erstellen,
StatMan
zieht die Funktion Rückschlüsse auf die fehlenden Daten. Das Verhalten kann sich je nach Datentyp der Spalte ändern.
Angenommen, Sie nehmen 100 Zeilen aus einer 10000-Zeilentabelle und erhalten 100 unterschiedliche Werte, um das Problem zu untersuchen. Eine Vermutung für den Rest der Daten in der Tabelle ist, dass es 10000 eindeutige Werte gibt. Eine andere Vermutung ist, dass es 100 verschiedene Werte gibt, aber jeder der Werte 100 Mal wiederholt wird. Die zweite Vermutung mag Ihnen unvernünftig erscheinen, der ich zustimmen werde. Wie balancieren Sie jedoch die beiden Ansätze, wenn die abgetasteten Daten ungleichmäßig verteilt zurückkehren? In der StatMan
Funktion sind einige von Microsoft dafür entwickelte Algorithmen enthalten . Die Algorithmen funktionieren möglicherweise nicht für alle Datenstörungen und alle Stichprobenstufen.
Lassen Sie uns ein relativ einfaches Beispiel durchgehen. Ich werde VARCHAR
Spalten wie in Ihrer Tabelle verwenden, um das gleiche Verhalten zu sehen. Ich werde jedoch nur einen verzerrten Wert zur Tabelle hinzufügen. Ich teste gegen SQL Server 2016 SP1. Beginnen Sie mit 100.000 Zeilen mit 100.000 eindeutigen Werten für die FK
Spalte:
DROP TABLE IF EXISTS X_STATS_SMALL;
CREATE TABLE X_STATS_SMALL (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID)
);
-- insert 100k rows
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.GetNums(100000);
CREATE INDEX IX_X_STATS_SMALL ON X_STATS_SMALL (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Hier einige Beispiele aus der Statistik:
╔═════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠═════════════╬════════════════╬═════════╣
║ 1.00001E-05 ║ 4.888205 ║ FK ║
║ 1.00001E-05 ║ 9.77641 ║ FK, ID ║
╚═════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 1005 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10648 ║ 665.0898 ║ 1 ║ 664 ║ 1.002173 ║
║ 10968 ║ 431.6008 ║ 1 ║ 432 ║ 1 ║
║ 11182 ║ 290.0924 ║ 1 ║ 290 ║ 1 ║
║ 1207 ║ 445.7517 ║ 1 ║ 446 ║ 1 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99989 ║ 318.3941 ║ 1 ║ 318 ║ 1 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
Für gleichmäßig verteilte Daten mit einem eindeutigen Wert pro Zeile erhalten wir eine genaue Dichte, selbst mit einer VARCHAR
Histogrammspalte und einer Stichprobengröße von 14294 Zeilen.
Fügen wir nun einen verzerrten Wert hinzu und aktualisieren die Statistiken erneut:
-- add 70k rows with a FK value of '35000'
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N + 100000 , '35000', REPLICATE('Z', 900)
FROM dbo.GetNums(70000);
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Bei einer Stichprobengröße von 17010 Zeilen ist die Dichte der ersten Spalte kleiner als sie sein sollte:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 6.811061E-05 ║ 4.935802 ║ FK ║
║ 5.882353E-06 ║ 10.28007 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦══════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬══════════╬═════════════════════╬════════════════╣
║ 10039 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10978 ║ 956.9945 ║ 1 ║ 138 ║ 6.954391 ║
║ 11472 ║ 621.0283 ║ 1 ║ 89 ║ 6.941863 ║
║ 1179 ║ 315.6046 ║ 1 ║ 46 ║ 6.907561 ║
║ 11909 ║ 91.62713 ║ 1 ║ 14 ║ 6.74198 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 35000 ║ 376.6893 ║ 69195.05 ║ 54 ║ 6.918834 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99966 ║ 325.7854 ║ 1 ║ 47 ║ 6.909731 ║
╚══════════════╩════════════╩══════════╩═════════════════════╩════════════════╝
Es ist überraschend, dass der AVG_RANGE_ROWS
Wert für alle Schritte bei etwa 6,9 ziemlich einheitlich ist, selbst für Schlüsseleimer, für die die Stichprobe keine doppelten Werte gefunden haben konnte. Ich weiß nicht warum das so ist. Die wahrscheinlichste Erklärung ist, dass der Algorithmus, mit dem die fehlenden Seiten erraten werden, mit dieser Datenverteilung und Stichprobengröße nicht gut zurechtkommt.
Wie bereits erwähnt, ist es möglich, die Dichte für die FK-Säule unter Verwendung des Histogramms zu berechnen. Die Summe der DISTINCT_RANGE_ROWS
Werte für alle Schritte beträgt 14497. Es gibt 179 Histogrammschritte, daher sollte die Dichte etwa 1 / (179 + 14497) = 0,00006813845 betragen, was ziemlich nahe am angegebenen Wert liegt.
Tests mit einer größeren Tabelle können zeigen, wie sich das Problem verschlimmern kann, wenn die Tabelle größer wird. Dieses Mal beginnen wir mit 1 M Zeilen:
DROP TABLE IF EXISTS X_STATS_LARGE;
CREATE TABLE X_STATS_LARGE (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID));
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.Getnums(1000000);
CREATE INDEX IX_X_STATS_LARGE ON X_STATS_LARGE (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Das Statistikobjekt ist noch nicht interessant. Die Dichte für FK
ist 1.025289E-06, was nahezu exakt ist (1.0E-06).
Fügen wir nun einen verzerrten Wert hinzu und aktualisieren die Statistiken erneut:
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N + 1000000 , '350000', REPLICATE('Z', 900)
FROM dbo.Getnums(700000);
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Bei einer Stichprobengröße von 45627 Zeilen ist die Dichte der ersten Spalte schlechter als zuvor:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 2.60051E-05 ║ 5.93563 ║ FK ║
║ 5.932542E-07 ║ 12.28485 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 100023 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 107142 ║ 8008.354 ║ 1 ║ 306 ║ 26.17787 ║
║ 110529 ║ 4361.357 ║ 1 ║ 168 ║ 26.02392 ║
║ 114558 ║ 3722.193 ║ 1 ║ 143 ║ 26.01217 ║
║ 116696 ║ 2556.658 ║ 1 ║ 98 ║ 25.97568 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 350000 ║ 5000.522 ║ 700435 ║ 192 ║ 26.03268 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 999956 ║ 2406.266 ║ 1 ║ 93 ║ 25.96841 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
AVG_RANGE_ROWS
ist bis zu 26. Interessanterweise liegt der Durchschnittswert für AVG_RANGE_ROWS
wieder bei 6,9 , wenn ich die Stichprobengröße auf 170100 Zeilen ändere (10-fache der anderen Tabelle) . Wenn Ihre Tabelle größer wird, wählt SQL Server eine kleinere Stichprobengröße aus, was bedeutet, dass er einen größeren Prozentsatz der Seiten in der Tabelle erraten muss. Dies kann statistische Probleme für bestimmte Arten von Datenversatz übertreiben.
Abschließend ist zu beachten, dass SQL Server die Dichte nicht wie folgt berechnet:
SELECT COUNT(DISTINCT FK) * 1700000. / COUNT(*) -- 1071198.9 distinct values for one run
FROM X_STATS_LARGE TABLESAMPLE (45627 ROWS);
Was für einige Datenverteilungen sehr genau sein wird. Stattdessen werden undokumentierte Algorithmen verwendet . In Ihrer Frage haben Sie gesagt, dass Ihre Daten nicht verzerrt sind, aber der INSTANCEELEMENTID
Wert mit der höchsten Anzahl zugeordneter IDs hat 12 und die häufigste Anzahl ist 1. Für die Zwecke der von diesen verwendeten Algorithmen Statman
könnte dies verzerrt sein.
An diesem Punkt können Sie nichts dagegen tun, außer Statistiken mit einer höheren Abtastrate zu sammeln. Eine gängige Strategie ist das Sammeln von Statistiken mit FULLSCAN
und NORECOMPUTE
. Sie können die Statistiken mit einem Job in jedem Intervall aktualisieren, das für Ihre Datenänderungsrate sinnvoll ist. Nach meiner Erfahrung ist ein FULLSCAN
Update nicht so schlecht, wie die meisten Leute denken, insbesondere gegen einen Index. SQL Server kann einfach den gesamten Index anstelle der gesamten Tabelle scannen (wie dies bei einer Zeilenspeichertabelle für eine nicht indizierte Spalte der Fall wäre). Darüber hinaus werden in SQL Serer 2014 nur FULLSCAN
Statistikaktualisierungen parallel durchgeführt, sodass eine FULLSCAN
Aktualisierung schneller abgeschlossen werden kann als einige Stichprobenaktualisierungen.
tablesample