Mysql
 sql >> Base de données >  >> RDS >> Mysql

Performances MySQL lors de la mise à jour de la ligne avec FK

Considérez le schéma suivant :(Rem stmts laissé pour votre commodité) :

-- 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;

Maintenant, nous avons 2 processus, P1 et P2. Le mieux est de tester où P1 est peut-être MySQL Workbench et P2 est une fenêtre de ligne de commande MySql. En d'autres termes, vous devez configurer cela en tant que connexions séparées et à droite. Vous devrez avoir un œil méticuleux pour les exécuter étape par étape de la manière appropriée (décrit dans le Narratif ci-dessous) et voyez son impact sur l'autre fenêtre de processus.

Considérez les requêtes suivantes, en gardant à l'esprit qu'une requête mysql non enveloppée dans une transaction explicite est elle-même une transaction implicite. Mais ci-dessous, j'ai balancé pour explicite :

Q1 :

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

Q2 :

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

Q3 :

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

Q4 :

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

Q5 (mélange de requêtes) :

SELECT * from weapons;
SELECT * from spies;

Récit

Q1 : Quand P1 commence à commencer Q1 , et arrive à place2, il a obtenu un verrou de mise à jour exclusif au niveau de la ligne dans les deux tables armes et espions pour la ligne id=1 (2 lignes au total, 1 ligne dans chaque table). Cela peut être prouvé par P2 commençant à exécuter Q3, atteignant la place 1, mais bloquant à la place 2, et n'étant libéré que lorsque P1 arrive à appeler COMMIT. Tout ce que je viens de dire à propos de P2 exécutant Q3 est identique pour P2 exécutant Q4. En résumé, sur l'écran P2, place2 se fige jusqu'au P1 Commit.

Une note à nouveau sur les transactions implicites. Votre vrai La requête Q1 va effectuer cela très rapidement et en sortir fera un commit implicite. Cependant, le paragraphe précédent le décompose si vous aviez des routines plus coûteuses en temps en cours d'exécution.

Q2 : Quand P1 commence à commencer Q2 , et arrive à place2, il a obtenu un verrou de mise à jour exclusif au niveau de la ligne dans les deux tables armes et espions pour la ligne id=1 (2 lignes au total, 1 ligne dans chaque table). Cependant, P2 n'a aucun problème avec Q3 bloquant les weapons , mais P2 a des problèmes de blocage lors de l'exécution de Q4 à place2 spies .

Ainsi, les différences entre Q1 et Q2 se résument à MySQL sachant que l'index FK n'est pas pertinent pour une colonne dans la MISE À JOUR, et le manuel indique que dans Note1 ci-dessous.

Lorsque P1 exécute Q1, P2 n'a aucun problème avec les types de requêtes Q5 en lecture seule non verrouillées. Les seuls problèmes sont ce que les rendus de données P2 voient en fonction du NIVEAU D'ISOLEMENT en place.

Remarque1 :À partir de la page du manuel MySQL intitulée Locks Set by Different Instructions SQL dans InnoDB :

Ce qui précède explique pourquoi le comportement de Q2 : est tel que P2 est libre d'effectuer une UPDATE ou d'acquérir un verrou momentané exclusif UPDATE sur les weapons . En effet, le moteur n'effectue pas de mise à jour avec P1 sur weapon_id et n'a donc pas de verrou au niveau de la ligne dans cette table.

Pour ramener cela à 50 000 pieds, la plus grande préoccupation est la durée pendant laquelle un verrou est maintenu soit dans une transaction implicite (une sans START/COMMIT), soit dans une transaction explicite avant un COMMIT. Un processus homologue peut être interdit d'acquérir son besoin d'une mise à jour en théorie indéfiniment. Mais chaque tentative d'acquisition de ce verrou est régie par son paramètre pour innodb_lock_wait_timeout . Cela signifie que, par défaut, après environ 60 secondes, il expire. Pour afficher votre paramètre, exécutez :

select @@innodb_lock_wait_timeout;

Pour moi, pour le moment, c'est 50 (secondes).