Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Dummies-Leitfaden zum Sperren von innodb

Hier sind meine Notizen aus der Arbeit mit dem MySQL-Support an einem kürzlich aufgetretenen, seltsamen Sperrproblem (Version 5.1.37):

Alle Zeilen und Indexeinträge, die durchlaufen werden, um zu den geänderten Zeilen zu gelangen, werden gesperrt. Es wird behandelt unter:

http://dev.mysql.com/doc /refman/5.1/en/innodb-locks-set.html

„Ein sperrendes Lesen, ein UPDATE oder ein DELETE setzen im Allgemeinen Datensatzsperren auf jeden Indexdatensatz, der bei der Verarbeitung der SQL-Anweisung gescannt wird. Es spielt keine Rolle, ob es WHERE-Bedingungen in der Anweisung gibt, die die Zeile ausschließen würden. InnoDB tut dies sich nicht an die genaue WHERE-Bedingung erinnern, sondern nur wissen, welche Indexbereiche gescannt wurden. ... Wenn Sie keine für Ihre Anweisung geeigneten Indizes haben und MySQL die gesamte Tabelle scannen muss, um die Anweisung zu verarbeiten, wird jede Zeile der Tabelle gesperrt, die in turn blockiert alle Einfügungen anderer Benutzer in die Tabelle."

Es ist. Eine oft hilfreiche Problemumgehung ist:

AKTUALISIEREN Sie die beliebige Tabelle, setzen Sie irgendetwas auf etwas, wo der Primärschlüssel enthalten ist (wählen Sie den Primärschlüssel aus der beliebigen Tabelle aus, in der die Einschränkungen nach Primärschlüssel geordnet sind);

Die innere Auswahl muss keine Sperren nehmen und das Update hat dann weniger Arbeit für die Aktualisierung. Die order by-Klausel stellt sicher, dass die Aktualisierung in der Reihenfolge der Primärschlüssel erfolgt, um der physischen Reihenfolge von InnoDB zu entsprechen, was der schnellste Weg ist.

Wenn es sich um eine große Anzahl von Zeilen handelt, wie in Ihrem Fall, kann es besser sein, das Auswahlergebnis in einer temporären Tabelle mit einer hinzugefügten Flag-Spalte zu speichern. Wählen Sie dann aus der temporären Tabelle aus, wo das Flag nicht gesetzt ist, um jeden Stapel zu erhalten. Führen Sie Updates mit einem Limit von beispielsweise 1000 oder 10000 aus und setzen Sie das Flag für den Batch nach dem Update. Die Begrenzungen halten die Sperrmenge auf einem tolerierbaren Niveau, während die ausgewählte Arbeit nur einmal durchgeführt werden muss. Commit nach jedem Batch, um die Sperren freizugeben.

Sie können diese Arbeit auch beschleunigen, indem Sie vor jedem Aktualisierungsstapel eine ausgewählte Summe einer nicht indizierten Spalte erstellen. Dadurch werden die Datenseiten in den Pufferpool geladen, ohne Sperren zu nehmen. Dann wird die Sperrung für eine kürzere Zeitspanne andauern, da keine Festplattenlesevorgänge erfolgen.

Das ist nicht immer praktisch, aber wenn es so ist, kann es sehr hilfreich sein. Wenn Sie es nicht in Stapeln tun können, können Sie zumindest zuerst die Auswahl versuchen, um die Daten vorab zu laden, wenn sie klein genug sind, um in den Pufferpool zu passen.

Verwenden Sie nach Möglichkeit den Transaktionsisolationsmodus READ COMMITTED. Siehe:

http://dev.mysql.com/doc/refman /5.1/en/set-transaction.html

Um diese reduzierte Sperrung zu erreichen, ist die Verwendung von zeilenbasierter binärer Protokollierung erforderlich (anstelle der standardmäßigen auf Anweisungen basierenden binären Protokollierung).

Zwei bekannte Probleme:

  1. Unterabfragen können manchmal nicht optimal optimiert sein. In diesem Fall handelte es sich um eine unerwünschte abhängige Unterabfrage - der von mir gemachte Vorschlag, eine Unterabfrage zu verwenden, stellte sich daher im Vergleich zur Alternative in diesem Fall als wenig hilfreich heraus.

  2. Löschungen und Aktualisierungen haben nicht den gleichen Umfang an Abfrageplänen wie ausgewählte Anweisungen, daher ist es manchmal schwierig, sie richtig zu optimieren, ohne die Ergebnisse zu messen, um genau herauszufinden, was sie tun.

Beides verbessert sich allmählich. Dieser Fehler ist ein Beispiel, bei dem wir gerade die für ein Update verfügbaren Optimierungen verbessert haben, obwohl die Änderungen erheblich sind und es noch der QA unterzogen wird, um sicherzustellen, dass es keine großen nachteiligen Auswirkungen hat:

http://bugs.mysql.com/bug.php?id=36569