Wir haben festgestellt, dass mehrere Server Zeilenabschnitte in eine Tabelle in einer relationalen Datenbank einfügen, und ein Server liest die neuen Daten gelegentlich aus der Tabelle. (Die Tabelle ist konzeptionell eine Art Logfile - Daten werden nur eingefügt, aber nie geändert, und der lesende Server zeigt ein Ende des Protokolls.) Gibt es eine Möglichkeit, dass der lesende Server nur die neuen Daten liest? Wir können die Tabelle (n) beliebig strukturieren.
Einige Ideen, die mir in den Sinn kamen, aber nicht funktionieren, sind:
Das Markieren der Zeilen als gelesen passt nicht zu unserer Anwendung: Der lesende Server sollte die Datenbank nicht ändern. (Es ist nicht gut, in die Datenbank zu schreiben, um Dinge anzuzeigen, und es kann mehrere Sitzungen geben, in denen die Dinge angezeigt werden.)
Wir könnten in jeder Zeile, die mit der Datenbanksystemzeit gefüllt ist, einen Zeitstempel einfügen. Das Problem ist, dass dies nicht der Zeitstempel der Commit-Zeit, sondern der Einfügezeit ist. Wenn Sie die Datenbank "Geben Sie mir alle Werte zwischen jetzt und in 5 Minuten an" eingeben, können Sie sich nicht darauf verlassen, dass alle Werte vorhanden sind, da möglicherweise Transaktionen in Bearbeitung sind. Sie müssen später noch einmal nach den Werten in diesem Intervall fragen, was ich vermeiden wollte.
Wir könnten eine laufende Zeilenanzahl einfügen, die aus einer Sequenz gefüllt wurde. Das gleiche Problem beim Ausführen von Transaktionen tritt auf wie bei der Verwendung von Zeitstempeln.
Gibt es eine Lösung für das Problem, oder muss ich einige Heuristiken wie eine maximale Transaktionszeit anwenden und immer nach Werten fragen, die nach "jetzt - maximale Transaktionszeit" geschrieben sind und einige Daten zweimal lesen?
Falls es darauf ankommt: Wir verwenden Oracle dafür. Aber ich gehe davon aus, dass Antworten, die nur mit anderen Datenbanken arbeiten, von allgemeinem Interesse sind.
Die verwendete Datenbank wurde nicht angegeben, sodass nicht klar ist, ob die Lösung in eine vorhandene Bereitstellung integriert werden muss oder nicht. Es gibt einige Warteschlangen-Engines, die an MySQL angeschlossen werden können, die möglicherweise funktionieren könnten. Einer von ihnen ist Q4M . Einige kommerzielle Datenbanken wie Oracle verfügen über eine temporäre Datenbankfunktionalität, mit der die Transaktionszeit im Vergleich zur tatsächlichen Zeit in Echtzeit ermittelt werden kann.
Bei Verwendung von Oracle kann entweder die Pseudo-Spalte ora_rowscn
oder die nützliche Kombination scn_to_timestamp(ora_rowscn)
den Zeitstempel für den Zeitpunkt liefern, zu dem eine Zeile festgeschrieben wurde (der SCN, in dem sie stattfand). Alternativ dazu bietet der Oracle Workspace Manager auch Tabellen zur Versionsaktivierung, die im Prinzip folgendermaßen aussehen: Sie aktivieren die Versionsverwaltung für eine Tabelle mit DBMS_WM.EnableVersioning(...)
, Zeilen werden mit einem zusätzlichen WMSYS.WM_PERIOD(...)
-Feld eingefügt, das einen gültigen Zeitraum angibt, einen gültigen Bereich für Arbeitsbereich wird auf dem Reader DBMS_WM.SetValidTime(...)
festgelegt.
Sie können diese Funktionalität auch zu einem gewissen Grad fälschen, indem Sie Ihre Timestamp-Idee mit der Heuristik der Commit-Zeit verknüpfen. Die Idee ist einfach, die "gültige Zeit" als Spalte zusammen mit den Daten zu speichern, anstatt ein beliebiges Delta von jetzt () zu verwenden. Mit anderen Worten, eine sekundäre Timestamp-Spalte, die ein zukünftiges Datum (die "gültige Zeit") basierend auf einer Heuristik der Commit-Zeit + irgendein akzeptables Zeitfenster der Verzögerung (vielleicht die mittlere Commit-Zeit + die doppelte Standardabweichung) spezifizieren würde. Als Alternative kann eine Ceil () der mittleren Commit-Zeit verwendet werden ("zumindest die Commit-Zeit, aber das Aufrunden auf beispielsweise 30 Sekunden-Intervalle"). Letzteres würde die Zeitprotokollsätze effektiv quantisieren (zusammenfassen?). Es scheint nicht zu anders, aber auf diese Weise können Sie keine redundanten Zeilen lesen. Es löst auch das Problem, dass die Leseanwendung die Commit-Zeiten der Schreibanwendung nicht genau kennen kann, ohne viel mehr Code zu schreiben.
MS SQL hat seine spezifische Lösung:
Sie können eine Spalte des Datentyps Zeilversion zur Tabelle hinzufügen. Diese Spalte wird automatisch von der Engine bei jeder update / insert-Anweisung für verwandte Zeilen aktualisiert.
Wenn der Writer ReadCommitted
isolation level verwendet, kann der Leser ReadUncommitted
isolation level verwenden (es muss also nicht auf das Ende aller Transaktionen gewartet werden, bevor irgendwelche Ergebnisse zurückgegeben werden), sondern mit der Abfrage wie folgt:
Dabei ist @LastKnownVersion
die maximale Zeilenversion, die vom Leser verarbeitet wird, und MIN_ACTIVE_ROWVERSION()
ist eine integrierte MS SQL-Funktion, die die minimale Zeilenversionsnummer zurückgibt, die sich noch in der Transaktion befindet.
Also mit dieser Lösung, auch wenn Sie ID = 4 Committed aber ID = 3 noch nicht haben, werden nur Zeilen zurückgegeben, die vor ID = 3 geändert wurden, da ihre Version genau MIN_ACTIVE_ROWVERSION()
ist.
Der Vorteil dieser Methode besteht darin, dass der Leser nicht darauf warten muss, dass die Transaktion ausgeführt wird, bevor Ergebnisse erzielt werden, was bei vielen Writern entscheidend sein kann. (Reader könnte für immer gesperrt sein.)
Dies ist eine mögliche Lösung, abhängig von Ihrer Situation usw.
Haben Sie eine Spalte namens "read_timestamp", die Null ist, sobald eine Zeile gelesen wird, aktualisiert der Lesevorgang es auf einen Zeitstempel ungleich Null.
Der Leser fragt diese Tabelle mit "where read_timestamp ist null" ab.
Eine einfachere Lösung wäre, mit einer Schätzung zu gehen (d. h. diese Zeile könnte bereits als Art der Einschränkung angesehen worden sein). Daher würden Sie jederzeit die "letzten 50 Zeilen" oder die "Zeilen anzeigen, die in den letzten 10 Minuten angekommen sind" (mit der Ungenauigkeit, dass ein anderer Log-Viewer diese bereits gezogen hat).
Eine dritte Lösung wäre, diese Zeilen mit einem Back-End-Prozess einer Warteschlange zuzuführen: Jedes Lesen einer Zeile bewirkt, dass die Zeile aus der Warteschlange verschwindet (weil es sich um eine Pop-Operation handelt). Daher kann eine Zeile nur einmal angezeigt werden (first-come-first-served).
Ich würde sagen, dass Ihre Idee für Zeitstempel gültig ist, aber anstatt nach einem Bereich zu fragen, fragen Sie einfach nach einer bestimmten Zeit nach allen Werten nach . Sie sollten alle verfügbaren -Werte in der Datenbank in dem zuletzt ausgewählten Zeitraum abrufen. Offensichtlich funktioniert es nicht für noch laufende Transaktionen, die nicht protokolliert wurden ... Sie müssen jedoch nur eine einfache Abfrage durchführen.
Bearbeiten:
Sie müssen sicherstellen, dass die Zeitmarkenwerte pro Zeile eindeutig sind. In diesem Fall müssen Sie nur den letzten Zeitstempelwert verfolgen, den Sie aus der Datenbank gelesen haben. Die nächste Abfrage für die Datenbank enthält alle Werte nach . Sie werden keine Daten verpassen und auch keine Duplikate lesen. Alle laufenden Transaktionen wurden während einer Abfrage nicht in der Datenbank gespeichert, sodass Sie sie bei der nächsten Abfrage der Datenbank nicht vergessen.
Erstellen Sie eine weitere Tabelle LOG_REVISION
. Es enthält eine einzelne Zeile (ein INTEGER
).
Der Protokollierungsprozess sollte diese Tabelle lesen und die Nummer, die er dort findet, zu jedem Protokolldatensatz hinzufügen. Sperren Sie die Zeile, bis Sie die Transaktion festgeschrieben haben.
Der Lesevorgang sollte zuerst die LOG_REVISION
aktualisieren, indem Sie die Zahl inkrementieren und dann alle Zeilen lesen, die die alte LOG_REVISION
haben.
[EDIT] Es gibt zwei weitere Möglichkeiten:
Erstellen Sie eine ID-Sequenz für die Protokolltabelle, sodass jedes Protokoll eine eindeutige ID hat. Wenn der Leser die Logs liest, zeichnet er irgendwo die höchste ID auf. Das nächste Mal wird es alle IDs nach der letzten aufgezeichneten ID erhalten. Jede laufende Transaktion wird kein Problem sein, da Sie sie nicht in der Ergebnismenge erhalten. Sie werden im nächsten Lauf gesammelt werden.
also, wenn du hast:
%Vor%Dann hast du alle diese Logs geholt und 3 als letzte ID gefunden. Und beim nächsten Lauf:
Wählen Sie die ID, loggen Sie sich aus den Logs aus, wo id & gt; last_recorded id order von id #id ist 3
%Vor%und notiere 6 als deine letzte aufgezeichnete ID. Ich denke immer noch, dass es gut ist, die Daten zu behalten, als das Protokoll auch gemacht wurde.
EDIT ok, um alles so zu fangen, müssen Sie eine Menge aller Aufzeichnungen behalten, die an einem alternativen Standort gelesen werden und dann die Differenz des gelesenen Satzes gegen die aktive Protokolltabelle erhalten. Wenn Sie die Protokolltabelle nicht berühren können, müssen Sie nur mit Mengen arbeiten und herausfinden, was nicht in einer der Mengen enthalten ist.
Tags und Links database-design