Die UPDATE-Abfragen in Ihren beiden vorherigen Fragen ( Frage 1 , Frage 2 ) treffen die Tabelle 'people' von PRIMARY KEY mit Sperrung auf Zeilenebene. Dies habe ich bereits in Frage 1 am 6. Juni 2011 um 10:03 Uhr festgestellt
Alle Transaktionen durchlaufen den PRIMARY-Schlüssel. Da der PRIMARY ein Clustered-Index in InnoDB ist, sind der PRIMARY-Schlüssel und die Zeile selbst zusammen. Durchqueren einer Reihe und und der PRIMARY KEY sind also ein und dasselbe. Daher ist jede Indexsperre für den PRIMARY KEY auch eine Sperre auf Zeilenebene.
Noch wurde nicht an etwas anderes gedacht, das den Indizes Langsamkeit zuschreiben kann: Die Verwendung von NON-UNIQUE-Indizes in InnoDB. Bei jeder indizierten Suche in InnoDB mit nicht eindeutigen Indizes ist auch die Zeilen-ID jeder Zeile mit dem nicht eindeutigen Schlüssel verknüpft. Die rowID stammt im Wesentlichen aus dem Clustered Index . Aktualisieren von nicht eindeutigen Indizes MUSS IMMER mit dem Clustered-Index interagieren, AUCH WENN DIE TABELLE KEINEN PRIMÄREN SCHLÜSSEL HAT.
Eine weitere Überlegung ist das Verwalten von BTREE-Knoten in einem Index. Manchmal ist die Seitenteilung von Knoten erforderlich. Alle Einträge im BTREE-Knoten nicht eindeutiger Indizes enthalten nicht eindeutige Felder zuzüglich der Zeilen-ID im Clustered-Index. Um die Aufteilung solcher BTREE-Seiten ordnungsgemäß zu verringern, ohne die Datenintegrität zu beeinträchtigen, muss die der rowID zugeordnete Zeile intern eine Sperre auf Zeilenebene aufweisen.
Wenn die 'people'-Tabelle viele nicht eindeutige Indizes enthält, bereiten Sie sich darauf vor, eine große Anzahl von Indexseiten im Tablespace zu haben und von Zeit zu Zeit winzige kleine Zeilensperren auf Sie zukommen zu lassen.
Es gibt noch einen anderen Faktor, der nicht so offensichtlich ist: die Schlüsselbevölkerung
Wenn ein Index gefüllt wird, können die Schlüsselwerte, aus denen sich die Indizes zusammensetzen, mit der Zeit schief werden und dazu führen, dass der MySQL Query Optimizer von Schlüsselsuchen zu Indexprüfungen und schließlich zu vollständigen Tabellensuchen wechselt. Dies können Sie nur steuern, wenn Sie die Tabelle mit neuen Indizes neu entwerfen, um die einseitige Ausrichtung der Schlüssel zu kompensieren. Bitte geben Sie die Tabellenstruktur für die 'people'-Tabelle, die Anzahl der' people'-Tabellen und die Ausgabe der show-Indizes für die 'people'-Tabelle an .
Selbst wenn Abfragen nur den PRIMARY KEY verwenden, muss für die Einseitigkeit von Schlüsseln in nicht eindeutigen Indizes noch ein BTREE-Ausgleich und eine Seitenteilung erfolgen. Eine solche BTREE-Verwaltung führt zu einer merklichen Verlangsamung aufgrund von zeitweiligen Sperren auf Zeilenebene, die Sie nicht beabsichtigt hatten.
UPDATE 2011-06-14 22:19
Fragen aus Frage 1
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
Stellen Sie sich die Sequenz in Ereignissen vor
- Finde die Reihe mit dem PRIMARY KEY
- Sperren Sie die Zeile und den Clustered-Index
- Erstellen Sie MVCC-Daten für alle zu aktualisierenden Spalten
- Vier Spalten sind indiziert (email, company_id, iphone_device_id, picture_blob_id)
- Jeder Index erfordert eine BTREE-Verwaltung
- Innerhalb desselben Transaktionsbereichs wird versucht, die Schritte 1-5 in derselben Zeile zu wiederholen und dieselben Spalten zu aktualisieren (E-Mail in beiden Abfragen gleich, Unternehmens_ID in beiden Abfragen gleich, Bild_Blob_ID in beiden Abfragen gleich, iphone_Device_ID unterschiedlich).
Fragen aus Frage 2
UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;
UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;
Diese beiden Abfragen sind noch verwirrender, da mit der ersten Abfrage alles außer people_id 666 aktualisiert wird. Hunderte von Zeilen werden nur mit der ersten Abfrage schmerzhaft gesperrt. Die zweite Abfrage aktualisiert die people_id 666, die die Sequenz von Ereignissen ausführt. Bei der ersten Abfrage werden mit Ausnahme von people_id 666 für jede beteiligte Zeile die gleichen 5 Ereignissequenzen ausgeführt, aber der Index für iphone_device_id befindet sich auf einem interecept-Kurs mit zwei verschiedenen Abfragen. Jemand muss die BTREE-Seiten nach dem Prinzip "Wer zuerst kommt, mahlt zuerst" sperren.
Angesichts dieser beiden Fragenpaare in einem Kollisionskurs kann es für InnoDB oder jedes ACID-konforme RDBMS schwierig sein, möglicherweise dieselben BTREE-Seiten innerhalb eines Index zu sperren. Daher ist eine Indexverlangsamung das Schicksal dieser Abfragepaare, sofern Sie nicht garantieren können, dass die Abfragen mit AUTOCOMMIT = 1 ausgeführt werden, oder indem Sie Dirty Reads zulassen (obwohl solche Kollisionen READ-COMMITTED und READ-UNCOMMITED zu einem Albtraum für MVCC machen).
UPDATE 15.06.2011 10:29
@RedBlueThing: In den Abfragen von Frage 2 ist die erste Abfrage eine Bereichsabfrage, daher werden viele Zeilensperren erreicht. Beachten Sie auch, dass beide Abfragen versuchen, den gleichen Speicherplatz zu sperren. ID 0 Seite Nr. 4611 n Bits 152 werden im PRIMARY KEY, auch bekannt als Clustered Index, gesperrt.
Um sicherzustellen, dass Ihre App basierend auf den von Ihnen erwarteten Ereignissen ausgeführt wird, gibt es zwei verschiedene Möglichkeiten:
Option 1) Konvertieren Sie diese Tabelle in MyISAM (zumindest auf einem Entwicklungsserver). Bei jedem UPDATE, INSERT und DELETE wird eine vollständige Tabellensperre auf der Basis von "first come first serve" festgelegt.
Option 2) Verwenden Sie die Isolationsstufe SERIALIZABLE . Dadurch werden alle beabsichtigten Zeilen im SHARED-Modus gesperrt.
Die erwartete Ereignissequenz wird mit diesen beiden alternativen Optionen unterbrochen oder erfolgreich ausgeführt. Wenn beide Optionen fehlschlagen, müssen Sie Ihre App überprüfen und die Reihenfolge der Ausführung Ihrer Abfragen priorisieren. Sobald Sie diese Priorität festgelegt haben, können Sie diese Optionen einfach rückgängig machen (für Option 1 kehren Sie zu InnoDB zurück, für Option 2 kehren Sie zur Standardisolationsstufe zurück [Verwendung von SERIALIZABLE beenden]).