Sollte ich die automatische Aktualisierungsstatistik in einem Data Warehousing-Szenario deaktivieren?


12

Ich habe 200 GB Data Warehouse in SQL Server.

Ich habe bei einigen Abfragen sehr langsame Ausführungszeiten festgestellt. Zum Beispiel 12 Stunden für eine einfache deleteAbfrage mit einem inner join.

Nachdem ich einige Nachforschungen mit den Ausführungsplänen angestellt habe, habe ich die Statistiken der beiden an der Abfrage beteiligten Tabellen mithilfe der WITH FULLSCANOption aktualisiert .

Die Abfrage wird jetzt in weniger als einer Sekunde ausgeführt, sodass die Statistiken anscheinend nicht auf dem neuesten Stand waren.

Ich erwäge, auto update statisticsdie Datenbank zu deaktivieren und UPDATE STATISTICSmanuell auszuführen, nachdem das Data Warehouse geladen wurde. Das Data Warehouse wird täglich und nachts schrittweise aus einem Quell-ERP-System geladen.

Habe ich Recht, wenn ich davon ausgehe, dass auto update statisticsData Warehousing-Szenarien nicht wirklich nützlich sind? Ist es sinnvoller, die Statistiken nach dem Laden der Daten manuell zu aktualisieren?


Dies ist eine sehr gute Lektüre über Statistiken: simple-talk.com/sql/performance/… Wir führen auch einen täglichen Job mit Olas Lösung ola.hallengren.com/… aus , um Statistiken über 1 TB Datenbank zu aktualisieren. Ich würde die Option zur automatischen Aktualisierung der Statistiken nicht deaktivieren.
Joy Walker

1
Es hängt stark davon ab, wie viele Datensätze sich in Ihren Tabellen befinden und wie viel Sie in einem Stapel hinzufügen. In einer 1b-Zeilentabelle, in der Sie 20 Millionen Zeilen pro Nacht hinzufügen, werden Ihre Statistiken etwa alle 10 Tage aktualisiert, was nicht besonders gut ist.
JNK

2
Nur zu Ihrer Information - es gibt ein Trace-Flag (2371), mit dem der Schwellenwert für die Statistikaktualisierung von festen 20% auf einen dynamischen Prozentsatz geändert werden kann. Weitere Informationen finden
Sie

Sehr informative Links, danke! @JNK: Ja, es ist eine große Datenbank. Die Staging-Tabelle hat mehr als 300 m Zeilen und je nach Tag fügen wir täglich etwas zwischen 1 m und 10 m ein. Ich werde mir die Statistiken genauer ansehen, wie es in der folgenden Antwort vorgeschlagen wurde.
Saso

Antworten:


11

Hier ist ein Whitepaper darüber, wann die automatische Aktualisierung der Statistiken erfolgt . Hier sind die wichtigsten Punkte im Hinblick auf automatische Aktualisierungen von Statistiken:

  • Die Tabellengröße ist von 0 auf> 0 Zeilen gestiegen (Test 1).
  • Die Anzahl der Zeilen in der Tabelle, in der die Statistiken erfasst wurden, betrug 500 oder weniger, und der colmodctr der führenden Spalte des Statistikobjekts hat sich seitdem um mehr als 500 geändert (Test 2).
  • Die Tabelle hatte mehr als 500 Zeilen, als die Statistiken erfasst wurden, und der colmodctr der führenden Spalte des Statistikobjekts hat sich um mehr als 500 + 20% der Anzahl der Zeilen in der Tabelle geändert, als die Statistiken erfasst wurden (Test 3). .

@JNK hat in einem Kommentar darauf hingewiesen, dass bei einer Milliarde Zeilen in einer Tabelle 20.000.5000 Schreibvorgänge in die erste Spalte der Statistik erforderlich sind, um eine Aktualisierung auszulösen.

Nehmen wir die folgende Struktur:

CREATE TABLE dbo.test_table (
    test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
    test_table_value VARCHAR(50), 
    test_table_value2 BIGINT, 
    test_table_value3 NUMERIC(10,2)
);

CREATE CLUSTERED INDEX cix_test_table ON dbo.test_table (test_table_id, test_table_value);

Jetzt können wir überprüfen, was im Statistikland passiert ist.

select * 
    from sys.stats
        where object_id = OBJECT_ID('dbo.test_table')

stat_container

Um jedoch festzustellen, ob dies ein aussagekräftiges statistisches Objekt ist, müssen wir:

dbcc show_statistics('dbo.test_table',cix_test_table)

Histogramm

Diese Statistik wurde also nicht aktualisiert. Das liegt daran, dass die Statistik anscheinend erst aktualisiert wird, wenn a SELECTauftritt, und selbst dann SELECTmuss die Statistik außerhalb dessen liegen, was SQL Server in seinem Histogramm hat. Hier ist ein Testskript, das ich ausgeführt habe, um dies zu testen:

    CREATE TABLE test_table (
        test_table_id INTEGER IDENTITY(1,1) NOT NULL, 
        test_table_value VARCHAR(50), 
        test_table_value2 BIGINT, 
        test_table_value3 NUMERIC(10,2)
    );

    CREATE CLUSTERED INDEX cix_test_table ON test_table (test_table_id, test_table_value);

    ALTER TABLE test_table ADD CONSTRAINT pk_test_table PRIMARY KEY  (test_table_id)

    SELECT * 
        FROM sys.stats
            WHERE object_id = OBJECT_ID('dbo.test_table')

    --DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table)
    DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

declare @test int = 0

WHILE @test < 1
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT 'one row|select < 1', * FROM test_table WHERE test_table_id < 1;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

SET @test = 1

WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '100 rows(add 99)|select < 100',* FROM test_table WHERE test_table_id < 100;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--get the table up to 500 rows/changes
WHILE @test < 500
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END
SELECT '500 rows(add 400)|select < 100',* FROM test_table WHERE test_table_id < 100;
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SELECT '500 rows(add 400)|select < 500',* FROM test_table WHERE test_table_id < 500;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 501
SET @test = 500;
WHILE @test < 501
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END


SELECT '501 rows(add 1)|select < 501',* FROM test_table WHERE test_table_id < 501;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 600
SET @test = 501;
WHILE @test < 600
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '600 rows (add 100)|select < 600',* FROM test_table WHERE test_table_id < 600;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 700
SET @test = 600;
WHILE @test < 700
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '700 rows (add 100)|select < 700', * FROM test_table WHERE test_table_id < 700;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;

--bump it to 1200
SET @test = 700;
WHILE @test < 1200
    BEGIN
        INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
            ('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
        SET @test = @test + 1;
    END

SELECT '1200 rows (add 500)|select < 1200',* FROM test_table WHERE test_table_id < 1200;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--DROP TABLE test_table

Anstatt die auto_update-Statistik blind zu deaktivieren, würde ich versuchen, Ihren Datensatz auf Versatz zu untersuchen. Wenn Ihre Daten einen erheblichen Versatz aufweisen, müssen Sie die Erstellung gefilterter Statistiken in Betracht ziehen und dann entscheiden, ob die manuelle Verwaltung von Statistikaktualisierungen die richtige Vorgehensweise ist.

Um auf Versatz zu analysieren, müssen Sie DBCC SHOW_STATISTICS(<stat_object>, <index_name>);(im obigen Skript ohne den WITH STAT_HEADER) die bestimmte Statistik- / Indexkombination ausführen , die Sie untersuchen möchten. Eine schnelle Möglichkeit, Ihren Versatz in Augenschein zu nehmen, besteht darin, das Histogramm (dritte Ergebnismenge) zu betrachten und die Varianz in Ihrem zu überprüfen EQ_ROWS. Wenn es ziemlich konsistent ist, ist Ihr Versatz minimal. Um dies zu erhöhen, sehen Sie sich die RANGE_ROWSSpalte und die Varianz dort an, da hiermit gemessen wird, wie viele Zeilen zwischen den einzelnen Schritten vorhanden sind. Schließlich können Sie das [All density]Ergebnis aus der DENSITY_VECTOR(zweiten Ergebnismenge) entnehmen und mit dem [Rows Sampled]Wert in der STAT_HEADER(ersten Ergebnismenge ) multiplizieren, um zu sehen, wie hoch die durchschnittliche Erwartung für eine Abfrage in dieser Spalte wäre. Sie vergleichen diesen Durchschnitt mit IhremEQ_ROWS Wenn es viele Stellen gibt, an denen es erheblich variiert, liegt ein Versatz vor.

Wenn Sie feststellen, dass Sie einen Versatz haben, müssen Sie einige gefilterte Statistiken zu den Bereichen erstellen, die sehr hoch sind, RANGE_ROWSdamit Sie zusätzliche Schritte für bessere Schätzungen dieser Werte angeben können.

Sobald Sie diese gefilterten Statistiken eingerichtet haben, können Sie die Möglichkeit prüfen, Statistiken manuell zu aktualisieren.


Vielen Dank für die umfassende Antwort. Ich habe nicht so viel Erfahrung mit den Statistiken und das scheint schwieriger zu sein, als ich dachte. Werde auf jeden Fall genauer hinschauen und deinen Richtlinien folgen.
Saso
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.