Statistiken verschwinden nach inkrementeller Aktualisierung


21

Wir haben eine große partitionierte SQL Server-Datenbank, die inkrementelle Statistiken verwendet. Alle Indizes sind partitioniert und ausgerichtet. Wenn wir versuchen, eine Partition online nach Partitionen neu zu erstellen, verschwinden alle Statistiken, nachdem der Index neu erstellt wurde.

Unten finden Sie ein Skript zum Replizieren des Problems in SQL Server 2014 mit der AdventureWorks2014-Datenbank.

--Example against AdventureWorks2014 Database

CREATE PARTITION FUNCTION TransactionRangePF1 (DATETIME)
AS RANGE RIGHT FOR VALUES 
(
   '20130501', '20130601', '20130701', '20130801', 
   '20130901', '20131001', '20131101', '20131201', 
   '20140101', '20140201', '20140301'
);
GO

CREATE PARTITION SCHEME TransactionsPS1 AS PARTITION TransactionRangePF1 TO 
(
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY]
);
GO

CREATE TABLE dbo.TransactionHistory 
(
  TransactionID        INT      NOT NULL, -- not bothering with IDENTITY here
  ProductID            INT      NOT NULL,
  ReferenceOrderID     INT      NOT NULL,
  ReferenceOrderLineID INT      NOT NULL DEFAULT (0),
  TransactionDate      DATETIME NOT NULL DEFAULT (GETDATE()),
  TransactionType      NCHAR(1) NOT NULL,
  Quantity             INT      NOT NULL,
  ActualCost           MONEY    NOT NULL,
  ModifiedDate         DATETIME NOT NULL DEFAULT (GETDATE()),
  CONSTRAINT CK_TransactionType 
    CHECK (UPPER(TransactionType) IN (N'W', N'S', N'P'))
) 
ON TransactionsPS1 (TransactionDate);


INSERT INTO dbo.TransactionHistory
SELECT * FROM Production.TransactionHistory
--  SELECT * FROM sys.partitions
--  WHERE object_id = OBJECT_ID('dbo.TransactionHistory');

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW, STATISTICS_INCREMENTAL=ON)  
  ON TransactionsPS1 (TransactionDate)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT 'Stats are avialable'  

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

PRINT 'After online index rebuild by partition stats are now gone'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Rebuild the stats with a rebuild for all paritions (this works)' 
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = ALL WITH (ONLINE = ON , DATA_COMPRESSION = ROW, 
  STATISTICS_INCREMENTAL = ON)

PRINT 'Stats are back'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Works correctly for an offline rebuild by partition'
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = OFF , DATA_COMPRESSION = ROW)

    --stats still there  
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT' stats are gone!!!!!!'

Wie gezeigt, können wir keine Indizes nach Partition online neu erstellen, ohne alle Statistiken für den Index zu verlieren. Dies ist ein wichtiges Wartungsproblem für uns. Es scheint fast so, als müsste die inkrementelle Statistikoption Teil der Syntax für die Neuerstellung eines einzelnen Index sein, oder die Onlineoption muss sie wie die Offlineoption richtig handhaben.

Bitte lassen Sie mich wissen, wenn ich etwas vermisse?

Aktualisierung:

Soweit wir inkrementelle Statistiken benötigen: Wir partitionieren nach einer internen Kunden-ID und nicht nach einem Datum. Wenn also ein neuer Client hinzugefügt wird (großer Daten-Backload), können wir einfach die Statistiken für die Partition aktualisieren und schnell verhindern, dass hässliche Pläne für diesen neuen Kunden erstellt werden. Ich denke, ich werde es bei Microsoft als Fehler einreichen und sehen, was sie zu sagen haben, und mit der Lösung fortfahren, einfach die Statistiken für diese Partition erneut abzutasten.

Fehlerbericht verbinden:

Statistiken verschwinden nach der Neuerstellung des Online-Index mit inkrementellen Statistiken

Update: Microsoft hat bestätigt, dass es sich um einen Fehler handelt.


1
Update: Microsoft hat mir heute Morgen eine E-Mail gesendet, dass dieser Fehler im nächsten CU-Update für SQL 2014
behoben wird

Weißt du, welche CUs das Problem behoben haben oder welche KBs sie in dieser E-Mail gemeldet haben? Versucht zu sehen, wann es repariert wurde.
Mbourgon

1
Es ist ziemlich sicher, dass es sich um den VSTS-Fehler Nr. 8046729 KB, Artikelnummer 3194959, handelt, der Teil von CU 9 für SQL Server 2014 SP1 war. Ein Link zur KB ist hier .
JasonR

Ja, das sieht so aus - und wurde erst letzten Monat auf 2016SP1 behoben. Vielen, vielen Dank!
mbourgon

Korrektur: gerade in 2016 SP1 CU2 behoben. Es passiert auf 2016 SP1 CU1.
mbourgon

Antworten:


17

Nicht sicher , ob es ein Fehler ist, per se , aber es ist auf jeden Fall ein interessantes Ereignis. Online-Neuerstellungen von Partitionen sind neu in SQL Server 2014, daher müssen möglicherweise einige Interna hiermit sortiert werden.

Hier ist meine beste Erklärung für dich. Inkrementelle Statistiken erfordern unbedingt, dass alle Partitionen mit der gleichen Rate abgetastet werden, damit beim Zusammenführen der Statistikseiten durch die Engine sichergestellt werden kann, dass die abgetastete Verteilung vergleichbar ist. REBUILDDaten müssen unbedingt mit einer Abtastrate von 100% abgetastet werden. Es gibt keine Garantie dafür, dass die 100% -Abtastrate auf Partition 9 immer die genaue Abtastrate der übrigen Partitionen ist. Aus diesem Grund sieht es so aus, als ob die Engine die Samples nicht zusammenführen kann und Sie einen leeren Statistik-Blob erhalten. Das Statistikobjekt ist jedoch noch vorhanden:

select 
    check_time = sysdatetime(),                         
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
    and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'

Sie können den Blob mit einer beliebigen Anzahl von Mitteln füllen:

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;

oder

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);

Sie können auch warten, bis AutoStats bei der ersten Kompilierung eines Abfrageplans mit diesem Objekt aktualisiert wurde:

-- look at my creative query
select * 
from dbo.TransactionHistory
where TransactionDate = '20140101';

Having said all das, dieser erhellende Beitrag von Erin Stellato Höhepunkten , was als Hauptmangel von inkrementalen Statistiken wahrgenommen werden muss kommen. Ihre Daten auf Partitionsebene werden vom Optimierer bei der Abfrageplanerstellung nicht verwendet, wodurch der vermutete Nutzen inkrementeller Statistiken verringert wird. Was ist der aktuelle Nutzen von inkrementellen Statistiken? Ich würde behaupten, dass ihr primärer Nutzen darin besteht, große Tabellen mit einer höheren Rate konsistenter zu testen als mit herkömmlichen Statistiken.

Anhand Ihres Beispiels sehen die Dinge folgendermaßen aus:

set statistics time on;

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

--SQL Server Execution Times:
--  CPU time = 94 ms,  elapsed time = 131 ms.


update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);

 --SQL Server Execution Times:
 --  CPU time = 0 ms,  elapsed time = 5 ms.

drop index IDX_ProductId On dbo.TransactionHistory;

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW)  
  ON [PRIMARY]

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

 --SQL Server Execution Times:
 --  CPU time = 76 ms,  elapsed time = 66 ms.

Ein Fullscan-Statistik-Update der inkrementellen Statistik kostet 131 ms. Ein Fullscan-Statistik-Update der nicht partitionierten Statistik kostet 66 ms. Die nicht ausgerichtete Statistik ist mit größter Wahrscheinlichkeit langsamer, da durch das Zusammenführen der einzelnen Statistikseiten mit dem Haupthistogramm ein Mehraufwand entsteht. Mit dem partitionierten Statistikobjekt können wir jedoch eine Partition aktualisieren und sie in 5 ms wieder mit dem Haupthistogramm-Blob zusammenführen. An diesem Punkt steht der Administrator mit der inkrementellen Statistik vor einer Entscheidung. Sie können die Gesamtwartungszeit für Statistiken verkürzen, indem sie normalerweise nur Partitionen aktualisieren, die aktualisiert werden müssen, oder sie können mit höheren Abtastraten experimentieren, sodass möglicherweise mehr Zeilen im gleichen Zeitraum wie in ihrem vorherigen Wartungszeitraum abgetastet werden. Ersteres ermöglicht eine Atempause im Wartungsfenster, letzteres kann Statistiken auf einem sehr großen Tisch an einen Ort verschieben, an dem Abfragen bessere Pläne auf der Grundlage genauerer Statistiken erhalten. Dies ist keine Garantie und Ihr Kilometerstand kann variieren.

Der Leser kann erkennen, dass 66 ms keine schmerzhafte Statistikaktualisierungszeit für diese Tabelle darstellt. Daher habe ich versucht, einen Test für den Stapelaustauschdatensatz einzurichten. Es gibt 6.418.608 Posts (ohne StackOverflow-Posts und alle Posts aus 2012 - ein Datenfehler meinerseits) in dem kürzlich von mir heruntergeladenen Dump.

Ich habe die Daten aufgeteilt nach [CreationDate]weil ... Demo.

Hier sind einige Zeitangaben für einige hübsche Standardszenarien (100% - Indexwiederherstellung, Standardeinstellung - automatische Aktualisierung der Statistiken oder UPDATE STATISTICSohne festgelegte Abtastrate):

  • Nicht inkrementelle Statistik mit Fullscan erstellen: CPU-Zeit = 23500 ms, verstrichene Zeit = 22521 ms.
  • Inkrementelle Statistik mit Vollabtastung erstellen: CPU-Zeit = 20406 ms, verstrichene Zeit = 15413 ms.
  • Aktualisieren Sie die nicht inkrementelle Statistik mit der Standardabtastrate: CPU-Zeit = 406 ms, verstrichene Zeit = 408 ms.
  • Inkrementelle Statistik mit Standardabtastrate aktualisieren: CPU-Zeit = 453 ms, verstrichene Zeit = 507 ms.

Nehmen wir an, wir sind ausgefeilter als diese Standardszenarien und haben entschieden, dass eine Abtastrate von 10% die Mindestrate ist, mit der wir die erforderlichen Pläne erstellen und gleichzeitig die Wartungszeit auf einen angemessenen Zeitraum beschränken können.

  • Aktualisieren Sie die nicht inkrementelle Statistik mit Beispiel 10 Prozent: CPU-Zeit = 2344 ms, verstrichene Zeit = 2441 ms.
  • Inkrementelle Statistik mit Beispiel 10 Prozent aktualisieren: CPU-Zeit = 2344 ms, verstrichene Zeit = 2388 ms.

Bisher gibt es keinen klaren Vorteil einer inkrementellen Statistik. Wenn wir jedoch die undokumentierte sys.dm_db_stats_properties_internal() DMV (unten) nutzen, können Sie einen Einblick in die Partitionen erhalten, die Sie aktualisieren möchten. Nehmen wir an, wir haben Änderungen an den Daten in Partition 3 vorgenommen und möchten sicherstellen, dass die Statistiken für eingehende Abfragen aktuell sind. Hier sind unsere Optionen:

  • Standardmäßig nicht inkrementelles Update (auch das Standardverhalten von Auto-Stats Update): 408 ms.
  • Aktualisierung nicht inkrementell bei 10%: 2441 ms.
  • Inkrementelle Statistik aktualisieren, Partition 3 mit Resample (10% - unsere definierte Abtastrate): CPU-Zeit = 63 ms, verstrichene Zeit = 63 ms.

Hier müssen wir eine Entscheidung treffen. Nehmen wir den Gewinn einer 63 ms. Partitionsbasierte Aktualisierung der Statistiken, oder erhöhen wir die Samplerate noch weiter? Nehmen wir an, wir sind bereit, den ersten Stichprobenerfolg mit einer inkrementellen Statistik bei 50% anzunehmen:

  • Aktualisierung der inkrementellen Statistik um 50%: verstrichene Zeit = 16840 ms.
  • Update Incremental Statistics, Partition 3 mit Resample (50% - unsere neue Update-Zeit): verstrichene Zeit = 295 ms.

Wir sind in der Lage, viel mehr Daten abzutasten und möglicherweise das Optimierungsprogramm einzurichten, um bessere Vermutungen über unsere Daten anzustellen (obwohl noch keine Partitionsebenenstatistiken verwendet werden), und wir können dies jetzt schneller tun inkrementelle Statistiken.

Eine letzte lustige Sache, um herauszufinden, aber. Was ist mit synchronen Statistik-Updates? Wird die Abtastrate von 50% auch dann beibehalten, wenn die Autostaten aktiviert werden?

Ich habe Daten aus Partition 3 gelöscht und eine Abfrage für CreationDate ausgeführt und die Raten mit derselben Abfrage wie unten überprüft. Die 50% ige Abtastrate wurde beibehalten.

Um es kurz zu machen: Inkrementelle Statistiken können ein nützliches Werkzeug sein, das über die richtige Menge an Gedanken und anfänglichen Einrichtungsarbeiten verfügt. Sie müssen jedoch das Problem kennen, das Sie lösen möchten, und müssen es dann entsprechend lösen. Wenn Sie schlechte Kardinalitätsschätzungen erhalten, können Sie möglicherweise bessere Pläne mit einer strategischen Abtastrate und einigen investierten Eingriffen erzielen. Sie erhalten jedoch nur einen kleinen Teil des Vorteils, da das verwendete Histogramm die einzelne Seite mit zusammengeführten Statistiken und nicht die Informationen auf Partitionsebene ist. Wenn Sie Schmerzen in Ihrem Wartungsfenster verspüren, können Ihnen möglicherweise inkrementelle Statistiken helfen, aber Sie müssen wahrscheinlich einen Eingriffsprozess für die Wartung einrichten, der einen hohen Grad an Aufmerksamkeit erfordert. Ungeachtet,:

  • Statistiken, die mit Indizes erstellt wurden, die nicht auf die Basistabelle partitioniert sind.
  • Statistiken, die für von AlwaysOn lesbare sekundäre Datenbanken erstellt wurden.
  • Statistiken, die für schreibgeschützte Datenbanken erstellt wurden.
  • Auf gefilterten Indizes erstellte Statistiken.
  • Auf Ansichten erstellte Statistiken.
  • Auf internen Tabellen erstellte Statistiken.
  • Statistiken, die mit räumlichen Indizes oder XML-Indizes erstellt wurden.

Hoffe das hilft

select 
    sysdatetime(),                          
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    parition_number = isnull(sp.partition_number,1),
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter = coalesce(sp.modification_counter, n1.modification_counter) 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
        and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
    and (
            (is_incremental = 0)
               or
            (is_incremental = 1 and sp.partition_number is not null)
         )
    and t.name = 'Posts'
    and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)
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.