Warum protokollieren Zeittabellen die Startzeit der Transaktion?


8

Beim Aktualisieren einer Zeile in einer temporären Tabelle werden die alten Werte für die Zeile in der Verlaufstabelle mit der Transaktionsstartzeit als gespeichert SysEndTime. Bei den neuen Werten in der aktuellen Tabelle beginnt die Transaktion mit der SysStartTime.

SysStartTimeund SysEndTimesind datetime2Spalten, die von temporalen Tabellen verwendet werden, um aufzuzeichnen, wann eine Zeile die aktuelle Version war. Die Transaktionsbeginnzeit ist die Zeit, zu der die Transaktion mit den Aktualisierungen gestartet wurde.

BOL sagt:

Die in den Spalten system datetime2 aufgezeichneten Zeiten basieren auf der Startzeit der Transaktion selbst. Beispielsweise wird für alle in eine einzelne Transaktion eingefügten Zeilen dieselbe UTC-Zeit in der Spalte aufgezeichnet, die dem Beginn des Zeitraums SYSTEM_TIME entspricht.

Beispiel: Ich aktualisiere alle Zeilen in meiner Orders-Tabelle um 20160707 11:00:00und die Transaktion dauert 5 Minuten. Dadurch wird für jede Zeile mit SysEndTimeas eine Zeile in der Verlaufstabelle erstellt 20160707 11:00:00. Alle Zeilen in der aktuellen Tabelle haben ein SysStartTimevon 20160707 11:00:00.

Wenn jemand eine Abfrage ausführen würde 20160707 11:01:00(während das Update ausgeführt wird), würde er die alten Werte sehen (unter der Annahme, dass die Standard-Isolationsstufe für das Lesen festgeschrieben ist).

Aber wenn jemand dann die AS OFSyntax verwenden würde, um die Zeittabelle so abzufragen, wie sie war 20160707 11:01:00, würde er die neuen Werte sehen, weil die ihre SysStartTimewären 20160707 11:00:00.

Für mich bedeutet dies, dass diese Zeilen nicht so angezeigt werden, wie sie zu diesem Zeitpunkt waren. Wenn die Transaktionsendzeit verwendet würde, wäre das Problem nicht vorhanden.

Fragen: Ist das beabsichtigt? Vermisse ich etwas

Der einzige Grund, warum ich glauben kann, dass die Startzeit der Transaktion verwendet wird, ist, dass sie zum Zeitpunkt des Transaktionsstarts nur "bekannt" ist. Es ist nicht bekannt, wann die Transaktion beim Start beendet wird, und es würde einige Zeit dauern, bis die Endzeit am Ende angewendet wird, wodurch die angewendete Endzeit ungültig wird. Macht das Sinn?

Auf diese Weise können Sie das Problem neu erstellen.


1
Sie haben Ihre eigene Frage beantwortet. Wenn Sie die Transaktionsendzeit verwenden, haben Sie am Ende der Transaktion ein weiteres Update: Das Update ist abgeschlossen 20160707 11:04:58und Sie aktualisieren jetzt alle Zeilen mit diesem Zeitstempel. Dieses Update wird jedoch auch einige Sekunden lang ausgeführt und endet 20160707 11:05:02nun mit welchem ​​Zeitstempel ist das richtige Ende der Transaktion? Oder nehmen Sie Read Uncommitedan 20160707 11:05:00, Sie haben at verwendet und Zeilen zurückgegeben, aber später werden AS OFsie nicht angezeigt.
dnoeth

@dnoeth Ja, ich denke, diese 'Frage' ist eher eine Klarstellung meiner Theorie.
James Anderson

Ich habe mich nicht mit der Implementierung von SQL Server befasst, aber Teradata hatte jahrelang bi-temporale Tabellen und ich empfehle immer, diese Fallstudie von Richard Snodgrass (dem Typ, der temporäre Abfragen "erfunden" hat) zu lesen. Sie basiert auf Teradatas SQL-Syntax vor ANSI , aber die Konzepte sind die gleichen: cs.ulb.ac.be/public/_media/teaching/infoh415/…
dnoeth

Antworten:


4

Die Idee ist, die logische Zeit gegen die physische Zeit zu verfolgen. Logisch bezieht sich einfach darauf, was ein Benutzer / eine App zum Zeitpunkt des Einfügens / Aktualisierens / Löschens erwartet. Die Tatsache, dass der DML-Vorgang aus irgendeinem Grund eine Weile dauern kann, ist für einen Benutzer nicht aussagekräftig oder sogar leicht zu bestimmen und zu verstehen. Wenn Sie jemals einem Buchhalter (ich habe) den Streit zwischen Schloss und Schloss erklären mussten, ist dies eine vergleichbare Situation.

Wenn Bob beispielsweise der App "mitteilt", dass alle Mitarbeiter in Bobs Abteilung 42 US-Dollar pro Minute verdienen 20160707 11:00:00, erwartet Bob (und seine Mitarbeiter), dass ab diesem Zeitpunkt die Bezahlung aller Mitarbeiter mit 42 US-Dollar pro Minute berechnet wird. Bob ist es egal, dass die App dafür 2 Lese- und 6 Schreibvorgänge pro Mitarbeiter in der Datenbank ausführen muss. Die Daten- und Protokolldateien befinden sich auf einer Reihe von RAID-5-SATA II-Laufwerken, sodass dies etwa 7 Minuten dauert die Aufgabe für alle 256 Mitarbeiter von Bob zu beenden. Bob, sein Buchhalter und der Lohnbuchhalter sorgen dafür, dass alle seine Mitarbeiter ab 42 USD / min bezahlt werden 20160707 11:00:00. Andernfalls sind die Mitarbeiter, die um aktualisiert wurden 20160707 11:00:01, leicht verärgert, während diejenigen, deren Aufzeichnungen um aktualisiert wurden 20160707 11:00:07, außerhalb der Personalabteilung versammelt werden.

Es gibt gültige Anwendungsfälle zum Verfolgen der physischen Zeit wie Debugging und Forensik, aber für den Endbenutzer ist dies im Allgemeinen bedeutungslos. Das Tlog speichert sowohl Bestell- als auch Zeitinformationen für jede der Schreibvorgänge (unter anderem), sodass es vorhanden ist, wenn Sie wissen, wie es aussehen soll.


Schöne Punkte. Ich denke, die Technologie ist nur für bestimmte Anwendungsfälle wie den von Ihnen erwähnten geeignet. Aus den oben genannten Gründen scheint es nicht geeignet zu sein, Preis- oder Aktienwerte zu verfolgen, die sich in sehr kurzer Zeit ändern können.
James Anderson

Nicht wirklich. Das ist ein Perf- und Scale-Problem. Zeittabellen funktionieren weiterhin, wenn Sie den Zeitpunkt des Aktienkurses beibehalten müssen. Sie müssen nur sicherstellen, dass die Einsätze sehr körnig sind und in einem sehr kleinen Fenster ausgeführt werden können. Andernfalls werden nachfolgende Änderungen blockiert. Wenn die eingehende Rate hoch genug ist, treten Zeitüberschreitungen und potenzieller Datenverlust auf, wenn die App Wiederholungsversuche nicht verarbeiten kann. Wenn Sie die DB über Fusion IO oder mit speicheroptimierten Tabellen ausführen, können Sie problemlos Zehntausende von Einfügungen pro Sekunde bis weit über hunderttausend pro Sekunde verarbeiten.
SQLmojoe

3

Ich glaube, dass dies tatsächlich ein Konstruktionsfehler ist, wenn auch einer, der nicht spezifisch für SQL Server 2016 ist, da alle anderen vorhandenen Implementierungen von temporalen Tabellen (soweit ich weiß) denselben Fehler aufweisen. Die Probleme, die dadurch bei Zeittabellen auftreten können, sind ziemlich schwerwiegend. Das Szenario in Ihrem Beispiel ist mild im Vergleich zu dem, was im Allgemeinen schief gehen kann:

Unterbrochene Fremdschlüsselreferenzen : Angenommen, wir haben zwei temporale Tabellen, wobei Tabelle A einen Fremdschlüsselverweis auf Tabelle B hat. Nehmen wir nun an, wir haben zwei Transaktionen, die beide auf einer Isolationsstufe READ COMMITTED ausgeführt werden: Transaktion 1 beginnt vor Transaktion 2, Transaktion 2 Fügt eine Zeile in Tabelle B ein und schreibt fest, dann fügt Transaktion 1 eine Zeile in Tabelle A mit einem Verweis auf die neu hinzugefügte Zeile von B ein. Da das Hinzufügen der neuen Zeile zu B bereits festgeschrieben wurde, ist die Fremdschlüsselbedingung erfüllt und die Transaktion 1 kann erfolgreich festschreiben. Wenn wir jedoch die Datenbank "AS OF" zwischen dem Beginn von Transaktion 1 und dem Beginn von Transaktion 2 anzeigen würden, würden wir Tabelle A mit einem Verweis auf eine Zeile von B sehen, die nicht existiert. Also in diesem FallDie Zeittabelle bietet eine inkonsistente Ansicht der Datenbank . Dies war natürlich nicht die Absicht des SQL: 2011-Standards, der besagt:

Historische Systemzeilen in einer systemversionierten Tabelle bilden unveränderliche Momentaufnahmen der Vergangenheit. Alle Einschränkungen, die beim Erstellen einer historischen Systemzeile wirksam waren, wurden bereits überprüft, als diese Zeile eine aktuelle Systemzeile war, sodass keine Einschränkungen für historische Systemzeilen erzwungen werden müssen.

Nicht eindeutige Primärschlüssel : Angenommen, wir haben eine Tabelle mit einem Primärschlüssel und zwei Transaktionen, beide auf der Isolationsstufe READ COMMITTED, in der Folgendes geschieht: Nachdem Transaktion 1 beginnt, aber bevor sie diese Tabelle berührt, löscht Transaktion 2 eine bestimmte Zeile der Tabelle und Commits. Anschließend fügt Transaktion 1 eine neue Zeile mit demselben Primärschlüssel ein wie der gelöschte. Dies geht gut durch, aber wenn Sie sich die Tabelle ab einem Zeitpunkt zwischen dem Beginn von Transaktion 1 und dem Beginn von Transaktion 2 ansehen, werden zwei Zeilen mit demselben Primärschlüssel angezeigt.

Fehler bei gleichzeitigen Aktualisierungen : Nehmen wir an, wir haben eine Tabelle und zwei Transaktionen, die beide dieselbe Zeile aktualisieren, wiederum auf der Isolationsstufe READ COMMITTED. Transaktion 1 beginnt zuerst, aber Transaktion 2 ist die erste, die die Zeile aktualisiert. Transaktion 2 wird dann festgeschrieben, und Transaktion 1 führt dann eine andere Aktualisierung der Zeile durch und schreibt fest. Dies ist alles in Ordnung, außer dass, wenn dies eine temporäre Tabelle ist, bei Ausführung der Aktualisierung in Transaktion 1, wenn das System die erforderliche Zeile in die Verlaufstabelle einfügt, die generierte SysStartTime die Startzeit von Transaktion 2 ist, während die SysEndTime ist die Startzeit von Transaktion 1, die kein gültiges Zeitintervall ist, da die SysEndTime vor der SysStartTime liegen würde. In diesem Fall gibt SQL Server einen Fehler aus und setzt die Transaktion zurück (siehe zdiese Diskussion ). Dies ist sehr unangenehm, da auf der Isolationsstufe READ COMMITTED nicht zu erwarten ist, dass Parallelitätsprobleme zu völligen Fehlern führen, was bedeutet, dass Anwendungen nicht unbedingt auf Wiederholungsversuche vorbereitet sind. Dies steht insbesondere im Widerspruch zu einer "Garantie" in der Microsoft-Dokumentation:

Dieses Verhalten garantiert, dass Ihre Legacy-Anwendungen weiterhin funktionieren, wenn Sie die Systemversionierung für Tabellen aktivieren, die von der Versionierung profitieren. ( Link )

Andere Implementierungen von Zeittabellen haben sich mit diesem Szenario befasst (zwei gleichzeitige Transaktionen, die dieselbe Zeile aktualisieren), indem sie die Option angeboten haben, die Zeitstempel automatisch "anzupassen", wenn sie ungültig sind (siehe hier und hier ). Dies ist eine hässliche Problemumgehung, da dies die unglückliche Folge hat, dass die Atomizität von Transaktionen unterbrochen wird, da bei anderen Anweisungen innerhalb derselben Transaktionen die Zeitstempel im Allgemeinen nicht auf die gleiche Weise angepasst werden. Wenn wir bei dieser Problemumgehung die Datenbank zu bestimmten Zeitpunkten "AB" anzeigen, werden möglicherweise teilweise ausgeführte Transaktionen angezeigt.

Lösung: Sie haben bereits die offensichtliche Lösung vorgeschlagen, bei der die Implementierung die Transaktionsendzeit (dh die Festschreibungszeit) anstelle der Startzeit verwendet. Ja, es stimmt, wenn wir eine Anweisung mitten in einer Transaktion ausführen, ist es unmöglich zu wissen, wie hoch die Festschreibungszeit sein wird (wie in der Zukunft oder möglicherweise nicht einmal vorhanden, wenn die Transaktion gerollt würde zurück). Dies bedeutet jedoch nicht, dass die Lösung nicht umsetzbar ist. es muss nur anders gemacht werden. Wenn Sie beispielsweise eine UPDATE- oder DELETE-Anweisung ausführen, kann das System beim Erstellen der Verlaufszeile anstelle einer Startzeit nur die aktuelle Transaktions-ID eingeben, und die ID kann später vom System nach dem Festschreiben der Transaktion in einen Zeitstempel konvertiert werden .

Im Zusammenhang mit dieser Art der Implementierung würde ich vorschlagen, dass vor dem Festschreiben der Transaktion alle Zeilen, die der Verlaufstabelle hinzugefügt werden, nicht für den Benutzer sichtbar sein sollten. Aus Benutzersicht sollte es einfach so aussehen, als würden diese Zeilen (mit dem Festschreibungszeitstempel) zum Zeitpunkt des Festschreibens hinzugefügt. Insbesondere wenn die Transaktion nie erfolgreich festgeschrieben wird, sollte sie niemals im Verlauf erscheinen. Dies steht natürlich nicht im Einklang mit dem SQL: 2011-Standard, der die Einfügungen in den Verlauf (einschließlich Zeitstempel) beschreibt, die zum Zeitpunkt der Anweisungen UPDATE und DELETE (im Gegensatz zum Zeitpunkt des Festschreibens) auftreten. Aber ich denke nicht, dass dies wirklich wichtig ist, wenn man bedenkt, dass der Standard aufgrund der oben beschriebenen Probleme nie richtig implementiert wurde (und wahrscheinlich auch nie sein kann).

Unter Leistungsgesichtspunkten erscheint es möglicherweise unerwünscht, dass das System zurückgehen und die Verlaufszeilen erneut aufrufen muss, um den Festschreibungszeitstempel auszufüllen. Aber je nachdem, wie dies gemacht wird, können die Kosten ziemlich niedrig sein. Ich bin nicht wirklich mit der internen Funktionsweise von SQL Server vertraut, aber PostgreSQL verwendet beispielsweise ein Write-Ahead-Protokoll, mit dem diese Aktualisierungen konsolidiert werden, wenn mehrere Aktualisierungen an denselben Teilen einer Tabelle durchgeführt werden Daten müssen nur einmal auf die physischen Tabellenseiten geschrieben werden - und das würde normalerweise in diesem Szenario zutreffen. Auf jeden Fall,

Da diese Art von System (soweit ich weiß) noch nie implementiert wurde, kann ich natürlich nicht sicher sagen, ob es funktionieren würde - vielleicht fehlt mir etwas -, aber ich sehe keinen Grund warum es nicht funktionieren konnte.


0

Zum Zeitpunkt des Festschreibens Ihrer Transaktion müssen alle Daten in Datenseiten geschrieben werden (im Speicher und auf der Festplatte in der Protokolldatei). Einschließlich SysStartTimeund SysEndTimeSpalten. Wie können Sie die Transaktionsendzeit ermitteln, bevor sie tatsächlich abgeschlossen ist?

Sofern Sie die Zukunft nicht vorhersagen können, ist die Verwendung der Transaktionsstartzeit die einzige Option, auch wenn diese möglicherweise weniger intuitiv ist.

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.