2016-11-18 5 views
1

Ich arbeite an einem Abrechnungssystem (C# -Code, MySQL Galera Cluster-Backend, InnoDB-Speicher-Engine) und habe Zweifel, wie eine wirklich eindeutige Sequenznummer für Rechnungen generiert werden kann. Normalerweise habe ich in anderen Systemen einen einzigartigen Dienst erstellt, um die Rechnungsnummern zu erhalten. Wenn eine Rechnung generiert wurde, habe ich diesen Dienst nach einer Nummer gefragt, und dieser Dienst garantierte den exklusiven Zugriff auf eine Tabelle, die den Zähler dort hätte überhaupt kein Problem.Korrekte Isolationsstufe für sequentiellen Zähler

Aber dieses neue System ist für hohe Verfügbarkeit geclustert, so dass dieser Ansatz nicht akzeptabel ist, da mehrere dieser Dienste ausgeführt werden müssen.

So die Logik Ich bin hier aplying ist dies:

  • Starttransaktion
  • erstellen Rechnung ohne Seriennummer
  • serielle Zähler abrufen
  • schreiben neue Zähler Tabelle
  • Update Rechnung
  • commit

Wenn ich nicht falsch liege, wenn eine andere Transaktion den Zähler aktualisiert, bevor die aktuelle Transaktion beendet ist, wird die Festschreibung eine Ausnahme auslösen und ich kann die Operation wiederholen, die die Reihenfolge der Rechnungsnummern gewährleistet.

Also meine Frage ist, welche ist die richtige Isolationsstufe, um dies zu erreichen? ist READ_COMMITED genug oder kann es zu Duplikaten führen? Oder gibt es einen besseren Ansatz dafür?

+0

Warum der Downvote? – Gusman

+0

Meinst du NDB Cluster? Galera-Cluster? Oder ein anderer Geschmack? –

+0

Es ist ein Galera-Cluster – Gusman

Antwort

1

Eigentlich würden beide Isolationsstufen Ihnen Ärger machen, wenn Sie nicht vorsichtig sind.

Neben technischen Unterschiede (zB wie viele Zeilen sie sperren), READ COMMITTED und REPEATABLE READ unterscheiden sich darin, wie sie die folgende Situation umgehen:

start transaction; 
select no from counters where type = 'INVOICE'; 
-- some other session changes the value and commits it 
select no from counters where type = 'INVOICE'; 

READ COMMITTED würden Sie zwei unterschiedliche Ergebnisse liefern, REPEATABLE READ würde Ihnen die alte Wert für beide select. Aber beide Isolationsstufen verhindern nicht, dass jemand den Wert ändert, also wollen Sie keine der beiden Situationen.

Das Wichtigste ist die Zeile, die Sie ändern werden zu sperren, so dass niemand sonst kann es ändern:

start transaction; 
select no from counters where type = 'INVOICE' for update; 
update counters set no = @newvalue where type = 'INVOICE'; 

oder tun zuerst das eigentliche Update, wenn die Berechnung einfach:

start transaction; 
update counters set no = no + 1 where type = 'INVOICE'; 
select no from counters where type = 'INVOICE'; 

Angenommen, Ihre Tabelle sieht so aus (und Sie fragen zB select max(no) from invoices nicht ab, um die letzte Nummer zu erhalten), funktionieren beide Isolationsstufen. Sie unterscheiden sich hauptsächlich darin, wie viele Zeilen sie sperren. Wenn Sie einen Index für (in meinem Beispiel) type haben, werden sie sich genau gleich verhalten.

Die Entscheidung würde dann von dem Rest Ihrer Abfragen abhängen.repeatable read ist normalerweise eine gute und sichere Wahl (und der Standard aus einem Grund); Wenn Sie es senken, müssen Sie möglicherweise über mögliche Probleme nachdenken, aber vielleicht eine Leistung/weniger Blockierung.

Sie haben nicht angegeben, wie Sie den Cluster einrichten, Sie müssen natürlich sicherstellen, dass alle dieselbe Tabelle verwenden oder unterschiedliche Offsets auf Ihren Mastern verwenden.

Zu Ihrer Frage, was passiert, wenn eine andere Transaktion versucht, den Wert zu ändern: Die zweite Transaktion wartet an dem Punkt, der eine gesperrte Ressource benötigt, bis die erste sie freigibt (normalerweise wenn sie fertig ist) eine Ausnahme, wenn ein Timeout erreicht wird (oder ein Deadlock erkannt wird).

+0

Danke für eine so detaillierte Antwort. Ich habe das jetzt klarer. Der Schlüssel ist das Update, ich kann den Zähler sicher vor der Auswahl erhöhen, ich mag diesen Ansatz. – Gusman

+0

@Gusman - empfehlen, dass Sie das Konzept sorgfältig überprüfen, vielleicht auf einer separaten Tabelle. Es gibt viele Nuancen in Galera, die Ihre strengen Anforderungen übertreffen könnten. –

+0

Die Seriennummer wird nur für diesen Zweck in einer isolierten Tabelle gespeichert, die Rechnungen befinden sich in einer anderen Tabelle, jedes Unternehmen hat seinen eigenen Datensatz in der seriellen Tabelle und ich verwende keine Art von Summen- oder dynamischen Daten zur Berechnung der Seriennummer nur eine einfache Ganzzahl, wenn also die Tabellensperre wirklich wie erwartet funktioniert, sollte es keine Probleme geben, nein? – Gusman