Database
 sql >> Datenbank >  >> RDS >> Database

Verstehen, was sp_updatestats wirklich aktualisiert

Als ich vor ein paar Wochen für eines unserer Immersion Events in Chicago war, hatte ein Teilnehmer eine Statistikfrage. Ich werde nicht auf alle Details rund um das Problem eingehen, aber der Teilnehmer erwähnte, dass die Statistiken mit sp_updatestats aktualisiert wurden . Dies ist eine Methode zum Aktualisieren von Statistiken, die ich nie empfohlen habe; Ich habe immer eine Kombination aus Indexneuaufbau und UPDATE STATISTICS empfohlen Statistiken aktuell zu halten. Wenn Sie mit sp_updatestats nicht vertraut sind , es ist ein Befehl, der für die gesamte Datenbank ausgeführt wird, um Statistiken zu aktualisieren. Aber wie Kimberly dem Teilnehmer sagte, sp_updatestats aktualisiert eine Statistik, solange eine Zeile geändert wurde. Wow. Ich habe Books Online sofort geöffnet und für sp_updatestats Sie werden Folgendes sehen:

sp_updatestats aktualisiert nur die Statistiken, die basierend auf den rowmodctr-Informationen in der sys.sysindexes-Katalogansicht aktualisiert werden müssen, wodurch unnötige Aktualisierungen von Statistiken für unveränderte Zeilen vermieden werden

Nun, ich gebe zu, ich habe eine Vermutung darüber aufgestellt, was „…aktualisierung erforderlich basierend auf den rowmodctr-Informationen in der sys.sysindexes-Katalogansicht…“ bedeutet. Ich bin davon ausgegangen, dass die Aktualisierungsentscheidung der gleichen Logik folgen würde wie die Option „Statistiken automatisch aktualisieren“, nämlich:

  • Die Tabellengröße ist von 0 auf>0 Zeilen gestiegen (Test 1).
  • Die Anzahl der Zeilen in der Tabelle, als die Statistiken erfasst wurden, war 500 oder weniger, und 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 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).

Diese Logik wird für sp_updatestats nicht befolgt . Tatsächlich ist die Logik so unglaublich einfach, dass es beängstigend ist:Wenn eine Zeile geändert wird, wird die Statistik aktualisiert. Eine Reihe. EINE REIHE. Was ist mein Anliegen? Ich mache mir Sorgen über den Overhead der Aktualisierung von Statistiken für eine Reihe von Statistiken, die nicht wirklich aktualisiert werden müssen. Schauen wir uns sp_updatestats genauer an .

Wir beginnen mit einer neuen Kopie der AdventureWorks2012-Datenbank, die Sie von Codeplex herunterladen können. Ich werde zuerst Zeilen in drei verschiedenen Tabellen aktualisieren:

USE [AdventureWorks2012];
GO
SET NOCOUNT ON;
GO
 
UPDATE [Production].[Product]
SET [Name] = 'Bike Chain'
WHERE [ProductID] = 952;
 
UPDATE [Person].[Person]
SET [LastName] = 'Cameron'
WHERE [LastName] = 'Diaz';
GO
 
INSERT INTO Sales.SalesReason
(Name, ReasonType, ModifiedDate)
VALUES('Stats', 'Test', GETDATE());
GO 10000

Wir haben eine Zeile in Production.Product geändert , 211 Zeilen in Person.Person , und wir haben 10.000 Zeilen zu Sales.SalesReason hinzugefügt . Wenn die sp_updatestats Prozedur folgte der gleichen Logik für Aktualisierungen wie die Option Statistik automatisch aktualisieren, dann nur Sales.SalesReason würde aktualisieren, weil es 10 Zeilen zu Beginn hatte (wohingegen die 211 Zeilen in Person.Person aktualisiert wurden stellen etwa ein Prozent der Tabelle dar). Wenn wir uns jedoch mit sp_updatestats befassen , können wir sehen, dass die verwendete Logik anders ist. Beachten Sie, dass ich die Anweisungen nur aus sp_updatestats extrahiere die verwendet werden, um zu bestimmen, welche Statistiken aktualisiert werden.

Ein Cursor iteriert durch alle benutzerdefinierten Tabellen und internen Tabellen in der Datenbank:

declare ms_crs_tnames cursor local fast_forward read_only for
select name, object_id, schema_id, type from sys.objects o
where o.type = 'U' or o.type = 'IT'
open ms_crs_tnames
fetch next from ms_crs_tnames into @table_name, @table_id, @sch_id, @table_type

Ein weiterer Cursor durchläuft die Statistiken für jede Tabelle und schließt Heaps und hypothetische Indizes und Statistiken aus. Beachten Sie, dass sys.sysindexes wird in sp_helpstats verwendet . Sysindexes ist eine SQL Server 2000-Systemtabelle und soll in einer zukünftigen Version von SQL Server entfernt werden. Dies ist interessant, da die andere Methode zum Bestimmen der aktualisierten Zeilen die sys.dm_db_stats_properties ist DMF, das nur in SQL 2008 R2 SP2 und SQL 2012 SP1 verfügbar ist.

set @index_names = cursor local fast_forward read_only for
select name, indid, rowmodctr
from sys.sysindexes
where id = @table_id
and indid > 0
and indexproperty(id, name, 'ishypothetical') = 0
order by indid

Nach ein wenig Vorbereitung und zusätzlicher Logik kommen wir zu einem IF -Anweisung, die offenbart, dass sp_updatestats filtert Statistiken heraus, bei denen keine Zeilen aktualisiert wurden … und bestätigt, dass die Statistik aktualisiert wird, selbst wenn nur eine Zeile geändert wurde. Es gibt auch eine Prüfung auf @is_ver_current , die durch eine eingebaute, interne Funktion bestimmt wird.

if ((@ind_rowmodctr <> 0) or ((@is_ver_current is not null) and (@is_ver_current = 0)))

Noch ein paar Prüfungen bezüglich Sampling und Kompatibilitätslevel und dann das UPDATE Anweisung wird für die Statistik ausgeführt. Bevor wir sp_updatestats tatsächlich ausführen, können wir sys.sysindexes abfragen um zu sehen, welche Statistiken aktualisiert werden:

SELECT [o].[name], [si].[indid], [si].[name], [si].[rowmodctr], [si].[rowcnt], [o].[type]
FROM [sys].[objects] [o]
JOIN [sys].[sysindexes] [si] ON [o].[object_id] = [si].[id]
WHERE ([o].[type] = 'U' OR [o].[type] = 'IT')
AND [si].[indid] > 0
AND [si].[rowmodctr] <> 0
ORDER BY [o].[type] DESC, [o].[name];

Zusätzlich zu den drei Tabellen, die wir geändert haben, gibt es eine weitere Statistik für eine Benutzertabelle (dbo.DatabaseLog ) und drei interne Statistiken, die aktualisiert werden:


Statistiken, die aktualisiert werden

Wenn wir sp_updatestats ausführen Für die AdventureWorks-Datenbank listet die Ausgabe jede Tabelle und die aktualisierten Statistiken auf. Die folgende Ausgabe wurde geändert, um nur aktualisierte Statistiken anzuzeigen:

Aktualisieren von [sys].[fulltext_avdl_1589580701]
[clust] wurde aktualisiert…
1 Index(e)/Statistik(en) wurden aktualisiert, 0 mussten nicht aktualisiert werden.

Aktualisieren von [dbo].[DatabaseLog]
[PK_DatabaseLog_DatabaseLogID] wurde aktualisiert…
1 Index(e)/Statistik(en) wurden aktualisiert, 0 mussten nicht aktualisiert werden.

Aktualisieren von [sys].[fulltext_avdl_1077578877]
[clust] wurde aktualisiert…
1 Index(e)/Statistik(en) wurden aktualisiert, 0 mussten nicht aktualisiert werden.

Aktualisieren von [Person].[Person]
[PK_Person_BusinessEntityID], Update ist nicht erforderlich…
[IX_Person_LastName_FirstName_MiddleName] wurde aktualisiert…
[AK_Person_rowguid], Update ist nicht erforderlich…
1 Index(e)/Statistik(en) wurden aktualisiert, 2 mussten nicht aktualisiert werden.

Aktualisieren von [Sales].[SalesReason]
[PK_SalesReason_SalesReasonID] wurde aktualisiert…
1 Index(e)/Statistik(en) wurden aktualisiert, 0 mussten nicht aktualisiert werden.

Aktualisiere [Produktion].[Produkt]
[PK_Product_ProductID], Update ist nicht erforderlich…
[AK_Product_ProductNumber], Update ist nicht erforderlich…
[AK_Product_Name] wurde aktualisiert…
[ AK_Product_rowguid], Update ist nicht notwendig…
[_WA_Sys_00000013_75A278F5], Update ist nicht notwendig…
[_WA_Sys_00000014_75A278F5], Update ist nicht notwendig…
[_WA_Sys_0000000D_75A278F5], Update ist nicht notwendig…
[_WA_Sys_0000000C_75A278F5], Aktualisierung ist nicht erforderlich…
1 Index(e)/Statistik(en) wurden aktualisiert, 7 mussten nicht aktualisiert werden.

Statistiken für alle Tabellen wurden aktualisiert.

Die letzte Zeile der Ausgabe ist etwas irreführend – die Statistiken für alle Tabellen wurden nicht aktualisiert, nur die Statistiken, bei denen eine oder mehrere Zeilen geändert wurden, wurden aktualisiert. Der Nachteil dabei ist wiederum, dass möglicherweise Ressourcen verwendet wurden, die nicht erforderlich waren. Wenn in einer Statistik nur eine Zeile geändert wurde, sollte sie aktualisiert werden? Nein. Wenn 10.000 Zeilen aktualisiert wurden, sollte es aktualisiert werden? Nun, das kommt darauf an. Wenn die Tabelle nur 5.000 Zeilen hat, dann absolut; Wenn die Tabelle 1 Million Zeilen hat, dann nein, da nur ein Prozent der Tabelle geändert wurde.

Das Wichtigste hier ist, dass Sie sp_updatestats verwenden Um Ihre Statistiken zu aktualisieren, verschwenden Sie höchstwahrscheinlich Ressourcen, einschließlich CPU, I/O und tempdb. Darüber hinaus dauert es einige Zeit, jede Statistik zu aktualisieren, und wenn Sie ein enges Wartungsfenster haben, haben Sie wahrscheinlich andere Wartungsaufgaben, die in dieser Zeit ausgeführt werden können, anstatt unnötige Aktualisierungen. Schließlich bieten Sie wahrscheinlich keine Leistungsvorteile, indem Sie Statistiken aktualisieren, wenn sich so wenige Zeilen geändert haben. Die Verteilungsänderung ist wahrscheinlich unbedeutend, wenn nur ein kleiner Prozentsatz der Zeilen geändert wurde, sodass sich das Histogramm und die Dichtewerte am Ende nicht so stark ändern. Denken Sie außerdem daran, dass das Aktualisieren von Statistiken Abfragepläne ungültig macht, die diese Statistiken verwenden. Wenn diese Abfragen ausgeführt werden, werden Pläne neu generiert, und der Plan ist wahrscheinlich genau derselbe wie zuvor, da es keine signifikante Änderung im Histogramm gab. Die Neukompilierung von Abfrageplänen ist mit Kosten verbunden – sie sind nicht immer leicht zu messen, sollten aber nicht ignoriert werden.

Eine bessere Methode zum Verwalten von Statistiken – da Sie Statistiken verwalten müssen – besteht darin, einen geplanten Job zu implementieren, der basierend auf den Prozentsätzen der geänderten Zeilen aktualisiert wird. Sie können die oben erwähnte Abfrage verwenden, die sys.sysindexes abfragt , oder Sie können die folgende Abfrage verwenden, die das neue DMF nutzt, das in SQL Server 2008 R2 SP2 und SQL Server 2012 SP1 hinzugefügt wurde:

SELECT [sch].[name] + '.' + [so].[name] AS [TableName] ,
[ss].[name] AS [Statistic],
[sp].[last_updated] AS [StatsLastUpdated] ,
[sp].[rows] AS [RowsInTable] ,
[sp].[rows_sampled] AS [RowsSampled] ,
[sp].[modification_counter] AS [RowModifications]
FROM [sys].[stats] [ss]
JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id]
JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id]
OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id],
[ss].[stats_id]) sp
WHERE [so].[type] = 'U'
AND [sp].[modification_counter] > 0
ORDER BY [sp].[last_updated] DESC;

Beachten Sie, dass verschiedene Tabellen unterschiedliche Schwellenwerte haben können und Sie die obige Abfrage für Ihre Datenbanken optimieren müssen. Bei einigen Tabellen kann es in Ordnung sein, zu warten, bis 15 % oder 20 % der Zeilen geändert wurden. Bei anderen müssen Sie jedoch möglicherweise um 10 % oder sogar 5 % aktualisieren, abhängig von den tatsächlichen Werten und ihrer Verzerrung. Es gibt keine Wunderwaffe. So sehr wir Absolutwerte lieben, in SQL Server existieren sie selten, und Statistiken bilden da keine Ausnahme. Sie möchten Auto Update Statistics trotzdem aktiviert lassen – es ist eine Sicherheit, die eingreift, wenn Sie etwas verpassen, genau wie Auto Growth für Ihre Datenbankdateien. Am besten ist es jedoch, Ihre Daten zu kennen und eine Methode zu implementieren, mit der Sie Statistiken basierend auf dem Prozentsatz der geänderten Zeilen aktualisieren können.