Frage

Wie Sie Rennbedingungen in MySQL stoppen? das Problem auf der Hand wird durch einen einfachen Algorithmus verursacht:

  1. Wählen Sie eine Zeile aus der Tabelle
  2. , wenn es nicht vorhanden ist, legen Sie sie

und dann entweder Sie eine doppelte Reihe bekommen, oder wenn Sie es über einzigartigen / Primärschlüssel verhindern, einen Fehler.

Jetzt normalerweise würde ich denken Transaktionen hier helfen, sondern weil die Zeile nicht existiert, kann die Transaktion nicht wirklich helfen (oder bin ich etwas fehlt?).

LOCK TABLE klingt wie ein Overkill, vor allem, wenn die Tabelle mehrmals pro Sekunde aktualisiert wird.

Die einzige andere Lösung, die ich denken kann, ist GET_LOCK () für jede unterschiedliche id, ist aber nicht es ein besserer Weg? Gibt es keine Skalierbarkeitsprobleme auch hier? Und auch, es für jede Tabelle zu tun klingt ein bisschen unnatürlich, da es wie ein sehr häufiges Problem in hohen Parallelität Datenbanken klingt für mich.

War es hilfreich?

Lösung

, was Sie wollen, ist LOCK TABLES

oder wenn das übertrieben scheint, wie etwa INSERT IGNORE mit a prüfen, ob die Zeile tatsächlich eingefügt wurde.

  

Wenn Sie das Schlüsselwort IGNORE, Fehler   die auftreten, während der Einsatz der Ausführung   Erklärung werden als Warnungen behandelt   statt.

Andere Tipps

Es scheint mich, Sie einen eindeutigen Index für Ihre ID-Spalte haben sollten, so dass ein wiederholter Einsatz würde einen Fehler auslösen, anstatt blendend zu werden wieder akzeptiert.

Das kann durch die Definition der ID als Primärschlüssel oder mit einem eindeutigen Index selbst durchgeführt werden.

Ich denke, die erste Frage, die Sie stellen müssen, warum hast du viele Threads genau die gleiche Arbeit tun? Warum sollten sie genau die gleiche Zeile einfügen müssen?

Danach beantwortet wird, denke ich, dass nur die Fehler zu ignorieren wird die performante Lösung, aber misst beiden Ansätze (GET_LOCK v / s Fehler ignorieren) und überzeugen Sie sich.

Es gibt keine andere Art und Weise, die ich kenne. Warum wollen Sie Fehler vermeiden? Sie haben noch für den Fall codieren, wenn eine andere Art von Fehler auftritt.

Wie staticsan sagt Transaktionen helfen aber, da sie in der Regel impliziert werden, wenn zwei Einsätze von verschiedenen Threads ausgeführt haben, werden sie innerhalb einer stillschweigenden Transaktionen sowohl konsistente und Blick auf die Datenbank sehen.

die gesamte Tabelle sperren ist in der Tat viel des Guten. Um den Effekt zu erhalten, die Sie wollen, müssen Sie etwas, das die litterature „Prädikat Schlösser“ nennt. Niemand hat jemals diejenigen außer gedruckt auf dem Papier, die wissenschaftlichen Studien veröffentlicht sind zu sehen auf. Die nächste beste Sache sind Sperren auf die „Zugangswege“ auf die Daten (in einigen DBMS: „Seitensperren“)

.

Einige Nicht-SQL-Systeme ermöglichen es Ihnen, beides zu tun (1) und (2) in einer einzigen Anweisung, mehr oder weniger die möglichen Rennbedingung Bedeutung von Ihrem O entstehen Ihren Ausführungs-Thread zur Aussetzung rechts zwischen (1) und (2) werden vollständig eliminiert.

Dennoch in Abwesenheit von Prädikat Sperren müssen solche Systeme noch zu irgendeiner Art von Sperrschema greifen, und je feiner die „Körnigkeit“ (/ „scope“) der Schlösser, desto besser für die Gleichzeitigkeit nimmt.

(Und zu dem Schluss:. Einige DBMS - vor allem diejenigen, die Sie müssen nicht bezahlen - tun in der Tat keine feinere Sperrgranularität bieten als „die gesamte Tabelle“)

Auf technischer Ebene, eine Transaktion wird hier helfen, weil andere Threads nicht die neue Zeile sehen, bis die Transaktion übermittelt.

Aber in der Praxis, die nicht lösen das Problem - es ist nur bewegt es. Ihre Anwendung muss nun prüfen, ob die Übertragung fehlschlägt und entscheiden, was zu tun ist. Ich hätte es normalerweise ein Rollback, was Sie getan haben, und die Transaktion neu starten, da nun die Zeile sichtbar. Dies ist, wie transaktionsbasierte Programmierer sollten arbeiten.

Ich lief in das gleiche Problem und das Netz für einen Moment gesucht:)

Schließlich kam ich mit der Lösung ähnelt Methode Dateisystem-Objekte in gemeinsamen (temporären) Verzeichnissen zu schaffen, um sicher zu öffnen temporäre Dateien:

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

Haben Sie keine Angst von busy-Schleife -. In der Regel wird es ausführen ein- oder zweimal

Sie verhindern, dass doppelte Zeilen sehr einfach durch eindeutige Indizes auf Ihren Tabellen zu setzen. Das hat nichts mit Sperrt oder GESCHÄFTE zu tun.

Haben Sie Sorge, wenn ein Einsatz schlägt fehl, weil es ein Duplikat ist? Benötigen Sie benachrichtigt werden, wenn es nicht funktioniert? Oder ist alles, was zählt, dass die Zeile eingefügt wurde, und es spielt keine Rolle, von wem oder wie viele Duplikate ausgefallen Einsätze?

Wenn Sie sich nicht, dann alles, was Sie brauchen, ist INSERT IGNORE. Es gibt keine Notwendigkeit, über Transaktionen oder Tabellensperren überhaupt zu denken.

InnoDB hat Zeilenebene Verriegelung automatisch, aber das gilt nur für Aktualisierungen und Löschungen. Sie haben Recht, dass es nicht zu Einsätzen gilt. Sie können nicht sperren, was existiert noch nicht!

Sie können explizit die gesamte Tabelle LOCK. Aber wenn Ihr Zweck Duplikate zu verhindern ist, dann ist es falsch Sie tun. Auch hier einen eindeutigen Index verwenden.

Wenn es eine Reihe von Änderungen vorgenommen werden soll, und Sie möchten ein Alles-oder-nichts Ergebnis (oder sogar eine Menge aller oder nichts Ergebnisse in einem größeren Alles-oder-nichts-Ergebnis), dann Transaktionen verwenden und Sicherungspunkte. Dann nutzen Sie ROLLBACK oder ROLLBACK TO SAVEPOINT *savepoint_name* zu Änderungen rückgängig zu machen, einschließlich Löschungen, Updates und Einsätze.

LOCK Tabellen sind kein Ersatz für Transaktionen, aber es ist die einzige Möglichkeit mit MyISAM-Tabellen, die Transaktionen nicht unterstützen. Sie können es auch mit InnoDB-Tabellen verwenden, wenn auf Zeilenebene Sperren nicht genug. Siehe Diese Seite für weitere Informationen über Verwendung von Transaktionen mit Sperrtabelle Anweisungen.

Ich habe ein ähnliches Problem. Ich habe eine Tabelle, die unter den meisten Umständen sollte eine eindeutige ticket_id Wert haben, aber es gibt einige Fälle, in denen ich Duplikate haben; nicht das beste Design, aber es ist, was es ist.

  1. Benutzer A überprüft, ob das Ticket reserviert ist, ist es nicht
  2. Benutzer B überprüft, ob das Ticket reserviert ist, ist es nicht
  3. Benutzer B fügt ein ‚reserviert‘ Datensatz in die Tabelle für das Ticket
  4. Benutzer A fügt ein 'reserviert' Datensatz in die Tabelle für das Ticket
  5. User B-Check für duplizieren? Ja, das ist mein Rekord neuere? Ja, lassen Sie es
  6. User Eine Überprüfung auf doppelte? Ja, das ist mein Rekord neuere? Nein, löschen

Benutzer B hat das Ticket reserviert, Benutzer A meldet zurück, dass das Ticket von jemand anderem getroffen wurde.

Der Schlüssel in meinem Fall ist, dass Sie ein Tie-Break benötigen, in meinem Fall ist es der Auto-Inkrement-ID auf der Zeile.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top