Christoph Muthmann
Architektur und Administration
Architektur und Administration
Hinter der Abkürzung VLF verbergen sich Virtual Log Files. Diese virtuellen Protokolldateien sind nicht besonders gut dokumentiert und man kann sie sich mit dokumentierten Methoden auch nicht anschauen. Vernachlässigen sollte man das Thema aber auch nicht unbedingt.
...
Zumindest sollte man ein paar Gedanken an die Auswirkungen verschwenden, wobei Server mit wenigen Datenbanken eigentlich nicht besonders betroffen sind. Schauen wir uns zuerst einmal an, worüber wir reden und danach, welche Auswirkungen diese VLFs auf die Performance haben können. Wer sich inhaltlich mit dem Transaktionslog auseinandersetzen möchte, wird vielleicht bei diesem älteren Artikel fündig.
Bereits bei der Anlage des Transaktionslogs wird ein Speicherbereich auf der Platte reserviert. Anders als viele annehmen, ist dies aber kein durchgängiger Bereich, sondern intern noch einmal in virtuelle Protokolldateien (VLFs) aufgeteilt. Wieviele VLFs nun erzeugt werden, hängt von der Größe des Bereichs ab, der in diesem Schritt allokiert werden soll.
| Größe des allokierten Segmentes | Anzahl von VLFs |
|---|---|
| ≤ 64 MB | 4 |
| > 64 MB und ≤ 1 GB | 8 |
| > 1 GB | 16 |
Möchte man also sein Transaktionslog mit 80 MB initial anlegen, werden 8 VLFs erzeugt, also mit 10 MB pro VLF. Möchte man es mit 16 GB anlegen, werden 16 VLFs erzeugt, jedes mit 1 GB!
Das gleiche Verfahren wird auch beim Wachstum angewendet. Jedes Mal wird geschaut, um welchen Wert das Transaktionslog wachsen soll, dann wird die Anzahl der VLFs errechnet (4, 8 oder 16) und dann wird der gewünsche Wert in entsprechend vielen VLFs angelegt.
Es gibt also einen signifikanten Unterschied zwischen einem Transaktionslog mit 16 GB, welches in einem Schritt angelegt wurde und einem Transaktionslog mit 16 GB, welches initial 80 MB hatte und dann in Schritten von 80 MB gewachsen ist. Einmal reden wir von 16 VLFs und im anderen Fall von ca. 1.640 VLFs.
Das ist leider nicht so einfach zu beantworten. Auf jeden Fall sollte man es möglichst nicht über ein automatisches Wachstum mit 10% versuchen. Das führt zu sehr vielen VLFs, wobei die Größen auch sehr unterschiedlich sind.
Der SQL Server meldet beim Start die Datenbanken, welche (seiner Meinung nach) zu viele VLFs haben. Im Errorolog findet man den folgenden Hinweis:
Database MyDB has more than 1000 virtual log files which is excessive. Too many virtual log files can cause long startup and backup times. Consider shrinking the log and using a different growth increment to reduce the number of virtual log files.
Anscheinend haben die Leute von Microsoft 1000 als magische Grenze ausgemacht. Im SAP blog hat man 10.000 als Grenze ausgemacht. Andere empfehlen Werte unter 100.
Jede Transaktion wird im Transaktionslog protokolliert. Das Transaktionslog wird gesichert und auch beim recovern gelesen. Also sollten wir Auswirkungen erwarten bei:
Aber sicher! Aber nur mit einer undokumentierten DBCC Option:
DBCC LOGINFO;
Als Ergebnis erhält man für jedes VLF eine Zeile zurück. Der Status 0 zeigt an, dass es nicht verwendet wird, der Status 2 zeigt an, dass dieses VLF derzeit belegt ist. die FSeqNo zeigt an, in welcher Reihenfolge diese aktiven VLFs geschrieben wurden. Das VLF mit der höchsten Nummer und dem Status 2 ist also der Punkt im Transaktionslog, wohin neue Transaktionen geschrieben werden. Wenn dieses VLF voll ist, wird das nächste freie VLF verwendet. Gibt es kein freies VLF mehr, wird das Transaktionslog (um 4, 8 oder 16 VLFs) erweitert. Kann das Log nicht erweitert werden, erhält man eine Fehlermeldung.
Die FileSize zeigt die Größe des VLF in Byte an. Hier gibt es schon mal Rundungsdifferenzen, so dass nicht alle VLFs gleich groß sind und auch zusammen ergeben die Werte, die in einem Schritt allokiert wurden nicht immer genau den gewünschten Wert.
Im Netz gibt es einige wenige Skripte und Tests, die sich mit den Performance-Auswirkungen beschäftigen. Ich habe mir als Basis das Skript vom MVP Tony Rogerson genommen, welches ursprünglich mit SQL Server 2000 getestet wurde. Für meine Tests habe ich die Anzahl der Datensätze verzehnfacht, um überhaupt Unterschiede sehen zu können. Dann habe ich noch die Zeiten für das Backup mit erfasst und auch für das DELETE scheint die Anzahl der VLFs von Bedeutung zu sein. Am Ende habe ich noch für ausgesuchte Fälle einen Service-Neustart durchgeführt, wie es auch das Microsoft Support Team durchgeführt hat. Ich habe den Test aber so variiert, dass das LDF vor dem Neustart gesichert wurde und der Service normal beendet wurde.
Dann habe ich die Tests so durchgeführt, dass beim ersten Durchlauf das Transaktionslog noch wachsen muss, um den maximalen Wert zu erreichen. Im zweiten Test wurde die Datenbank nicht neu angelegt, so dass auch kein Wachstumszeiten mehr angefallen sein können.
Aus den unten stehenden Zahlen wird auch deutlich, dass nicht alle Unterschiede wirklich durch VLFs erklärbar sind. Weder wurden dieses Tests mehrfach durchgeführt und gemittelt, noch möchte ich behaupten, dass mein Testserver (ein virtuelles System) ohne Beeinflussung von außen existieren würde. Aber Tendenzen sollten erkennbar sein.
Die Variante mit den meisten VLFs ist erwartungsgemäß am langsamsten, wobei hier die Zeiten für das Backup signifikant größer sind, als bei den anderen Beispielen. Das Wachstum um fast 40.000 VLFs für das Delete schlägt auch hier deutlich zu Buche.
Die anderen Varianten liegen dicht beieinander, wobei anscheinend die besten Zeiten beim Delete mit dem ausbleiben des Wachstums des Transaktionslogs einhergehen. Ein weiteres Argument für eine vorrausschauende Ressourcen-Planung, damit keine automatischen Wachstumsprozesse störend in den Produktionsbetrieb einwirken können.
Der größte Fehler wäre natürlich ein Shrinken des Transaktionslogs als Teil eines Wartungsplans, da man dies bei nächster Gelegenheit wieder mit schlechter Performance bezahlen muss.
Wenn wir auf das Wachstum des Transaktionslogs verzichten können, sehen alle Zeiten erst einmal besser aus. Auch die Unterschiede zwischen den Varianten schrumpfen. Gewinner ist die Variante mit 176 VLFs, wobei auch der nächstplazierte mit 472 VLFs nicht so schlecht aussieht.
Die Backup-Zeiten sind vergleichbar geblieben, da diese ja auch nicht vom Wachstum abhängig sind. Den Einfluss der VLFs auf die Backups sieht man am besten, wenn man in den jeweiligen Varianten das erste Backup des ersten Laufs mit dem ersten Backup des zweiten Laufs vergleicht. Beim zweiten Lauf haben wir ja bereits mit der maximalen Anzahl VLFs gestartet.
Die Variante mit 32 VLFs verliert deutlich beim Insert, Update und Delete gegenüber der schnellsten Variante. Die Backupzeiten sind hier nur geringfügig schlechter.
Wenige VLFs bedeuten also nicht unbedingt beste Backup-Zeiten!
Die schnellste Variante mit 176 VLFs ist immerhin noch 110 Sekunden besser, als die langsamste mit 52.436 VLFs.
Oh je! Hier dachte ich erst, es käme evtl. keine Meldung mehr und die Vergleiche sind nicht so einfach möglich. Aber nach über 2 Minuten kam dann doch noch die erlösende Meldung, dass die Datenbank mit den 52.436 VLFs nun endlich bereit wäre.
Hier zeigt sich ein deutlicher Einfluss der Anzahl VLFs auf die Performance des Serverneustarts. Selbst wenn es bei jeder Datenbank nur 10 Sekunden wären, die man gewinnen könnte, so würde dies bei einem Server mit 50 oder mehr Datenbanken einen gewaltigen Unterschied für die Verfügbarkeit machen.
Es ist auch nicht die Größe des LDF, die hier den Unterschied macht, denn die schnelle Variante hat sogar 16 GB auf 32 VLFs aufgeteilt.
Wenn man sich das SQL Server Errorlog nach einem Neustart anschaut und dann mal die Zeit zwischen "Starting up database '...'." und der nächsten Meldung für diese Datenbank ausrechnet, wird die Verzögerung deutlich. Man kann sich nun einfach mal eine der Datenbanken nehmen, die einem direkt ins Auge fallen und mit DBCC LOGINFO kontrollieren, wieviele VLFs hier vorhanden sind. Ab 1000 VLFs wird ja zudem eine Meldung im Errorolog angezeigt. Aber wer kontrolliert das schon? Immer? Gründlich?
Es gibt leider einen Bug, den Paul Randal in seinem blog beschreibt.
Wenn man also (in einer Version vor SQL Server 2012) das Wachstum des LDF auf ein Vielfaches von 4 GB stellt, bekommt man bei jedem zweiten Wachstum nur eine Vergrößerung von 31 KB. Ursache ist wohl ein Rundungsfehler im Code des SQL Servers.
Falls man so große Schritte benötigt, macht man am besten einen Bogen um die 4GB und verwendet lieber 4000 MB oder ein Vielfaches davon. Daher kommen auch die 8000 MB in meinem Beispiel oben!
Jetzt kommen wir also zu dem Teil, den man ausführen sollte, wenn man festgestellt hat, dass das Transaktionsprotokoll über zu viele oder zu wenige VLFs verfügt. Diese Schritte sollte man möglichst nicht in den Stoßzeiten der Produktion durchführen, da sie von der Produktion behindert werden und anders herum auch!
Dies ist jetzt mal eine Stelle, an der die Verkleinerung einer Datei Sinn macht. Möglichst nach einem Backup des Transaktionslogs führt man ein
DBCC SHRINKFILE(2);
für das Transaktionslog durch. Ggf. muss eine kleine Dummy-Transaktion gestartet werden, die den aktiven Teil des Logs an den Anfang bewegt, ein erneutes Backup des Logs durchgeführt werden und abschließend ein erneutes Shrinken ausgeführt werden. Beobachten lässt sich das alles mit DBCC LOGINFO.
Wenn man ein möglichst kleines Transaktionslog hat, kann man nun die Werte für das Log neu setzen. Z.B.:
ALTER DATABASE [myDB] Modify FILE
( NAME = N'myDB_log', SIZE = 200MB , FILEGROWTH = 200MB );
Setzt man in diesem Schritt bereits den Wert für SIZE auf den Endwert, erhält man ja nur wenige VLFs im Log. Besser ist es den Wert mehrfach zu erhöhen, bis der Zielwert erreicht ist, oder darauf zu warten, bis die User durch ihre Aktionen das Log wieder auf den gewünschten Wert vergrößert haben. Die Wartezeiten liegen dann allerdings bei den Usern.
| Print article | This entry was posted by Christoph Muthmann on 14.01.13 at 14:11:00 . Follow any responses to this post through RSS 2.0. |
15.01.13 @ 15:46:59
"Too many virtual log files can cause long startup and backup times" - eine unschöne Sache, wie ich aus eigener Erfahrung weiss! Klasse gemacht, Christoph!
CU
tosc