MySQL: une transaction va-t-elle verrouiller la ligne?

13

Je n'ai jamais essayé d'utiliser la transaction MySQL auparavant, je veux juste clarifier quelque chose.

Si deux utilisateurs exécutent une requête au moment très exact, comment MySQL traiterait-il cela? par exemple, les utilisateurs tentent de mettre à jour un enregistrement.

user1: mettre à jour l'ensemble de tables column = column - 4 where column_id = 1;

utilisateur2: mettre à jour l'ensemble de tables colonne = colonne - 7 où id_colonne = 1;

Maintenant, si j'utilise des transactions, MySQL choisira-t-il quelle requête sera exécutée en premier et verrouillera le deuxième utilisateur jusqu'à ce que la première requête soit validée? Sera-ce un verrou de table ou un verrou de ligne?

Et si un troisième utilisateur émet une instruction select? Quelle sera la valeur que MySQL renverra?

PS ce sera sur Innodb.

zer09
la source

Réponses:

17

Une seule instruction comme celle-ci fonctionne de la même manière avec MyISAM ou InnoDB, avec une transaction ou avec autocommit = ON. Il bloque suffisamment pour effectuer la requête, bloquant ainsi l'autre connexion. Une fois terminé, l'autre connexion se poursuit. Dans tous les cas, la colonne est rapidement décrémentée de 11.

Un troisième utilisateur peut voir la valeur décrémentée de 0 ou 4 ou 7 ou 11. Le "temps très exact" n'est pas vraiment possible car, à un moment donné de l'exécution de chaque instruction, un verrou à un seul thread est vérifié / défini / peu importe . Autrement dit, ils seront sérialisés, tellement vite que vous ne pouvez pas le voir.

InnoDB verrouille uniquement les lignes, pas les tables. (OK, l'instruction DDL fait des verrous plus audacieux.)

Ce qui devient plus intéressant, c'est une transaction qui modifie deux choses, ou qui prend un temps considérable:

Cas d'intention: article unique mais prenant du temps:

BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;

La sélection doit être écrite ainsi:

SELECT something  FOR UPDATE;

Cela indique aux autres connexions "J'ai l'intention de mettre à jour la ligne; ne me dérangez pas". (J'évoque cet exemple, car beaucoup de débutants manquent cette subtilité.)

Cas de blocage: jouer avec 2 choses:

BEGIN;    -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;

BEGIN;    -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;

Ceci est l'exemple classique d'une impasse - chacun attrape une chose et atteint l'autre chose. De toute évidence, cela ne peut pas fonctionner. Une transaction est tuée; l'autre se termine. Par conséquent, vous devez vérifier les erreurs afin de pouvoir les découvrir.

La réaction normale à un blocage est de rejouer l'intégralité de la transaction ayant échoué. D'ici là, l'autre connexion ne sera pas interférer et devrait se dérouler sans problème. (OK, une autre connexion pourrait créer un autre blocage.)

Cas de retard: Si les deux connexions saisissent plusieurs choses dans le même ordre, l'une peut être retardée jusqu'à ce que l'autre se termine. Pour éviter que cela "n'attende éternellement", il y a une valeur par défaut de 50 secondes innodb_lock_wait_timeout. Votre paire de simples UPDATEsest en fait un exemple de ce cas. L'un se terminera rapidement; l'autre est au point mort jusqu'au premier.

Notez comment un Deadlock peut (dans certains cas) être transformé en retard en ordonnant de manière cohérente les choses que vous touchez.

autocommit = 1: Avec ce paramètre et sans appel BEGIN, chaque instruction est effectivement:

BEGIN;
your statement
COMMIT;

autocommit = 0: c'est un problème qui attend de se produire. Lorsque vous effectuez une requête d'écriture, un BEGINest généré implicitement. Cependant, il est de votre responsabilité d'émettre éventuellement COMMIT. Si vous ne le faites pas, vous vous demanderez pourquoi votre système est bloqué. (Un autre bug de débutant commun.) Mon conseil: "Ne jamais utiliser =0".

Rick James
la source