Es gibt einige mögliche Szenarien, die leicht zu lösen sind, und ein schädliches, das es nicht ist.
Für einen Benutzer, der einen Wert eingibt und einige Zeit später denselben Wert eingibt, führt ein einfaches SELECT, bevor das INSERT das Problem erkennt. Dies funktioniert für den Fall, dass ein Benutzer einen Wert übermittelt und einige Zeit später ein anderer Benutzer denselben Wert übermittelt.
Wenn der Benutzer eine Liste von Werten mit Duplikaten - beispielsweise {ABC, DEF, ABC} - in einem einzigen Aufruf des Codes übermittelt, kann die Anwendung die Duplikate erkennen und filtern, wodurch möglicherweise ein Fehler ausgelöst wird. Sie müssen außerdem vor dem Einfügen überprüfen, ob die Datenbank keinen der eindeutigen Werte enthält.
Das schwierige Szenario besteht darin, dass sich der Schreibvorgang eines Benutzers gleichzeitig mit dem Schreibvorgang eines anderen Benutzers im DBMS befindet und denselben Wert schreibt. Dann haben Sie ein Rennen eine Bedingung zwischen ihnen. Da das DBMS (höchstwahrscheinlich - Sie sagen nicht, welches Sie verwenden) ein präventives Multitasking-System ist, kann jede Aufgabe zu jedem Zeitpunkt ihrer Ausführung angehalten werden. Das bedeutet, dass die Aufgabe von Benutzer1 prüfen kann, ob keine Zeile vorhanden ist, die Aufgabe von Benutzer2 prüfen kann, ob keine Zeile vorhanden ist, die Aufgabe von Benutzer1 diese Zeile einfügen kann und die Aufgabe von Benutzer2 diese Zeile einfügen kann. An jedem Punkt sind die Aufgaben individuell froh, dass sie das Richtige tun. Global tritt jedoch ein Fehler auf.
Normalerweise würde ein DBMS dies handhaben, indem es den fraglichen Wert sperrt. In diesem Problem erstellen Sie eine neue Zeile, sodass noch nichts gesperrt werden muss. Die Antwort ist eine Entfernungssperre. Wie dies nahelegt, wird ein Wertebereich gesperrt, unabhängig davon, ob sie derzeit vorhanden sind oder nicht. Nach dem Sperren kann eine andere Aufgabe erst dann auf diesen Bereich zugreifen, wenn die Sperre aufgehoben wird. Um Bereichssperren zu erhalten, müssen Sie die Isolationsstufe SERIALIZABLE angeben . Das Phänomen, dass sich eine andere Aufgabe nach der Überprüfung Ihrer Aufgabe hintereinander schleicht, wird als Phantomdatensätze bezeichnet .
Das Festlegen der Isolationsstufe für die gesamte Anwendung auf Serializable hat Auswirkungen. Der Durchsatz wird reduziert. Andere Rennbedingungen, die in der Vergangenheit gut genug funktionierten, können jetzt Fehler anzeigen. Ich würde vorschlagen, es auf die Verbindung zu setzen, die Ihren doppelt induzierenden Code ausführt, und den Rest der Anwendung unverändert zu lassen.
Eine codebasierte Alternative besteht darin, nach dem Schreiben und nicht vorher zu prüfen . Führen Sie also INSERT aus und zählen Sie die Anzahl der Zeilen mit diesem Hashwert. Wenn es Duplikate gibt, wird die Aktion zurückgesetzt. Dies kann einige perverse Ergebnisse haben. Angenommen, Aufgabe 1 schreibt dann Aufgabe 2. Dann prüft Aufgabe 1 und findet ein Duplikat. Es rollt zurück, obwohl es das erste war. In ähnlicher Weise können beide Aufgaben das Duplikat und beide Rollbacks erkennen. Aber zumindest haben Sie eine Nachricht, mit der Sie arbeiten können, einen Wiederholungsmechanismus und keine neuen Duplikate. Rollbacks sind verpönt, ähnlich wie Ausnahmen zur Steuerung des Programmflusses. Beachten Sie gut, dass alleDie Arbeit in der Transaktion wird zurückgesetzt, nicht nur das doppelte Schreiben. Und Sie müssen explizite Transaktionen haben, die die Parallelität verringern können. Die doppelte Überprüfung ist schrecklich langsam, es sei denn, Sie haben einen Index für den Hash. Wenn Sie dies tun, können Sie es auch zu einem einzigartigen machen!
Wie Sie kommentiert haben, ist die eigentliche Lösung ein eindeutiger Index. Es scheint mir, dass dies in Ihr Wartungsfenster passen sollte (obwohl Sie Ihr System natürlich am besten kennen). Angenommen, der Hash ist acht Bytes. Für einhundert Millionen Zeilen ist das ungefähr 1 GB. Die Erfahrung zeigt, dass ein vernünftiges Stück Hardware diese vielen Zeilen in ein oder zwei Minuten verarbeiten würde. Doppelte Überprüfungen und Eliminierungen tragen dazu bei, können jedoch im Voraus per Skript erstellt werden. Dies ist jedoch nur eine Seite.