ProblĂšme de verrou?

28 August 2007 par pébé

J’ai eu un petit problĂšme sympa Ă  rĂ©soudre. En rĂ©sumĂ©, nous avons 2 tables t1 et t2.

mysql> select * from t1;
+——+——+
| id | name |
+——+——+
| 1 | a |
| 2 | b |
| 3 | ab |
| 4 | bc |
+——+——+

mysql> select * from t2;
+————–+———+
| dependent_id | base_id |
+————–+———+
| 1 | 1 |
| 3 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 3 | 2 |
| 4 | 2 |
+————–+———+

Nous ouvrons une transaction (T1) et nous exĂ©cutons la requĂȘte suivante.

SELECT * FROM t1 JOIN t2 WHERE t1.id=t2.dependent_id AND t2.base_id=1 FOR UPDATE;

+——+——+————–+———+
| id | name | dependent_id | base_id |
+——+——+————–+———+
| 1 | a | 1 | 1 |
| 3 | ab | 3 | 1 |
+——+——+————–+———+

Nous ouvrons une transaction (T2) et nous exĂ©cutons la requĂȘte suivante.

SELECT * FROM roles WHERE id=2 FOR UPDATE;

Et alors que les 2 requĂȘtes ne sont pas sensĂ©es travailler sur les mĂȘme lignes, T2 attend la fin de T1? Comment est ce possible?

Pour comprendre vous devez analyser le plan d’exĂ©cution.

mysql> explain SELECT * FROM t1 JOIN t2 WHERE t1.id=t2.dependent_id AND t2.base_id=1 FOR UPDATE;

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 7 | Using where |
| 1 | SIMPLE | t1 | ALL | PRIMARY | NULL | NULL | NULL | 3 | Using where |

Voila l’erreur, MySQL fait un full table scan (type ALL) pour exĂ©cuter cette requĂȘte et ainsi bloque toutes les lignes. T2 est ainsi obliger d’attendre la fin de T1. Pourquoi?

Ayant seulement 4 entrĂ©es dans cette table, l’optimiseur de MySQL prĂ©fĂšre un faire un full table scan plutot qu’utiliser un index. De la j’ ai insĂ©rĂ© d’autre entrĂ©e dans la table t1, et le plan d’exĂ©cution a changĂ©.

| 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 217 | Using where |
| 1 | SIMPLE | t1 | eq_ref | PRIMARY | PRIMARY | 4 | t2.dependent_id | 1 | |

MySQL bloque ainsi seulement la PK et fait bien du ‘row level locking’. T2 peut aboutir sans problĂšme. CQFD!



Autres billets susceptibles de vous intéresser :
  • Les covering index, de la thĂ©orie Ă  la pratique avec MyISAM et InnoDB
  • CardinalitĂ©, sĂ©lectivitĂ© et distributivitĂ© d’un index MySQL : quel impact sur le plan d’exĂ©cution ?
  • GĂ©nĂ©rer un jeu de donnĂ©es : shell, mysqlslap, et procĂ©dure stockĂ©e
  • Installation du MySQL Cluster
  • mysql_secure_installation, utile mais non paramĂ©trable
  • 3 réponses à “ProblĂšme de verrou?”

    1. greg Says:

      Comme je sais que tout change entre chaque version de MySQL, peut on savoir sur quelle version cette démonstration est valable ?

    2. pébé Says:

      Salut Greg,
      je l’ai testĂ© sur la version utilisĂ© par la personne en question, soit MySQL 4.1.
      NĂ©anmoins, je ne pense pas que MySQL ai modifiĂ© le fonctionnement de l’optimiser. Si la table est petite, il va toujours avoir tendance Ă  faire un FULL TABLE scan que de s’embĂȘter avec des index.

    3. greg Says:

      TestĂ© avec succĂšs sur postgrsql 8.1 quelque soit le nombre d’enregistrements,
      ils ont par contre un autre bug (extrait de la doc):

      BEGIN;
      SELECT * FROM mytable WHERE key = 1 FOR UPDATE;
      SAVEPOINT s;
      UPDATE mytable SET … WHERE key = 1;
      ROLLBACK TO s;

      Le «rollback to s» va quand mĂȘme unlocker les rows de la table du prĂ©cĂ©dent SELECT.

    Laisser un commentaire