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

MySQL-Leistung beim Aktualisieren von Zeilen mit FK

Betrachten Sie das folgende Schema:(Rem stmts links für Ihre Bequemlichkeit) :

-- drop table if exists spies;
create table spies
(   id int primary key,
    weapon_id int not null,
    name varchar(100) not null,
    key(weapon_id),
    foreign key (weapon_id) references weapons(id)
)engine=InnoDB;

-- drop table if exists weapons;
create table weapons
(   id int primary key,
    name varchar(100) not null
)engine=InnoDB;

insert weapons(id,name) values (1,'slingshot'),(2,'Ruger');
insert spies(id,weapon_id,name) values (1,2,'Sally');
-- truncate table spies;

Jetzt haben wir 2 Prozesse, P1 und P2. Am besten testen Sie, wo P1 vielleicht MySQL Workbench und P2 ein MySql-Befehlszeilenfenster ist. Mit anderen Worten, Sie müssen dies als separate Verbindungen einrichten und richtig. Sie müssten ein akribisches Auge dafür haben, diese Schritt für Schritt in der richtigen Weise auszuführen (beschrieben in der Erzählung unten) und sehen Sie sich die Auswirkung auf das andere Prozessfenster an.

Betrachten Sie die folgenden Abfragen und denken Sie daran, dass eine MySQL-Abfrage, die nicht in eine explizite Transaktion eingeschlossen ist, selbst eine implizite Transaktion ist. Aber unten habe ich für explizit geschwungen:

F1:

START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond', weapon_id = 1 WHERE id = 1;
-- place2
COMMIT;

F2:

START TRANSACTION;
-- place1
UPDATE spies SET name = 'Bond' WHERE id = 1;
-- place2
COMMIT;

F3:

START TRANSACTION;
-- place1
SELECT id into @mine_to_use from weapons where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;

F4:

START TRANSACTION;
-- place1
SELECT id into @mine_to_use from spies where id=1 FOR UPDATE; -- place2
-- place3
COMMIT;

Q5 (Sammelsurium von Abfragen):

SELECT * from weapons;
SELECT * from spies;

Erzählung

F1: Wenn P1 beginnt, beginnt Q1 , und erreicht Platz 2, hat es eine exklusive Aktualisierungssperre auf Zeilenebene in beiden Tabellen Waffen und Spione für die Zeile id=1 erhalten (2 Zeilen insgesamt, 1 Zeile in jeder Tabelle). Dies kann dadurch bewiesen werden, dass P2 beginnt, Q3 zu laufen, auf Platz 1 gelangt, aber auf Platz 2 blockiert und erst freigegeben wird, wenn P1 dazu kommt, COMMIT aufzurufen. Alles, was ich gerade über P2 in Q3 gesagt habe, gilt auch für P2 in Q4. Zusammenfassend lässt sich sagen, dass place2 auf dem P2-Bildschirm bis zum P1-Commit einfriert.

Nochmals eine Anmerkung zu impliziten Transaktionen. Dein echter Die Q1-Abfrage wird dies sehr schnell ausführen, und wenn sie herauskommt, wird ein implizites Commit ausgeführt. Der vorherige Absatz bricht es jedoch auf, wenn Sie zeitaufwändigere Routinen ausführen müssen.

F2: Wenn P1 beginnt, beginnt Q2 , und erreicht Platz 2, hat es eine exklusive Aktualisierungssperre auf Zeilenebene in beiden Tabellen Waffen und Spione für die Zeile id=1 erhalten (2 Zeilen insgesamt, 1 Zeile in jeder Tabelle). P2 hat jedoch keine Probleme damit, dass Q3 weapons blockiert , aber P2 hat Blockierungsprobleme beim Ausführen von Q4 an place2 spies .

Die Unterschiede zwischen Q1 und Q2 beruhen also darauf, dass MySQL weiß, dass der FK-Index für eine Spalte im UPDATE nicht relevant ist, und das Handbuch gibt dies in Anmerkung1 an unten.

Wenn P1 Q1 ausführt, hat P2 keine Probleme mit dem Nur-Lese-Nicht-Sperre-Erwerben von Q5-Typen von Abfragen. Die einzigen Probleme sind, welche Datendarstellungen P2 basierend auf dem vorhandenen ISOLATIONSLEVEL sieht.

Anmerkung1 :Von der MySQL-Handbuchseite mit dem Titel Locks Set by Different SQL-Anweisungen in InnoDB :

Das obige ist der Grund für das Verhalten von Q2: ist so, dass P2 frei ist, ein UPDATE durchzuführen oder eine UPDATE-exklusive vorübergehende Sperre für weapons zu erwerben . Dies liegt daran, dass die Engine kein UPDATE mit P1 auf Weapon_id durchführt und daher keine Sperre auf Zeilenebene in dieser Tabelle hat.

Um dies auf 50.000 Fuß zurückzuziehen, ist die größte Sorge die Dauer, für die eine Sperre entweder in einer impliziten Transaktion (eine ohne START/COMMIT) oder einer expliziten Transaktion vor einem COMMIT gehalten wird. Einem Peer-Prozess kann theoretisch auf unbestimmte Zeit verboten werden, seinen Bedarf an einem UPDATE zu erwerben. Aber jeder Versuch, diese Sperre zu erlangen, wird durch seine Einstellung für innodb_lock_wait_timeout . Das bedeutet, dass es standardmäßig nach etwa 60 Sekunden abläuft. Führen Sie für eine Ansicht Ihrer Einstellung Folgendes aus:

select @@innodb_lock_wait_timeout;

Bei mir sind es im Moment 50 (Sekunden).