2010-03-29 8 views
7

J'ai un problème étrange (au moins pour moi :)) avec le verrouillage de MySQL.Mysql SELECT FOR UPDATE - problème étrange

J'ai une table:

create table `test` ( 
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1 

Avec ces données:

+ ---- +
| id |
+ ---- +
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 10 |
| 11 |
| 12 |
+ ---- +

Maintenant, j'ai 2 clients avec ces commandes exécutées au début:

set autocommit = 0;
Définir le niveau d'isolement de transaction de session sérialisable;
début;

Maintenant la partie la plus intéressante. Le premier client exécute cette requête: (fait l'intention d'insérer une ligne avec un identifiant égal à 9)

SELECT * à partir du test où id = 9 FOR UPDATE;
ensemble vide (0.00 sec)

Ensuite, le second client fait la même chose:

SELECT * de test où id = 9 FOR UPDATE;
ensemble vide (0.00 sec)

Ma question est: Pourquoi le second client ne bloque pas? Un verrou d'espace exclusif doit avoir été défini par la première requête car FOR UPDATE a été utilisé et le second client doit bloquer.

Si je me trompe, quelqu'un pourrait-il me dire comment le faire correctement?

La version MySql J'utilise est: 5.1.37-1ubuntu5.1

+0

Il ne bloque pas parce que vous avez verrouillé avec succès ** zéro lignes **. – Pacerier

Répondre

3

Parce qu'à ce moment-là, il est sûr de retourner le (vide) résultat - il n'y a pas de verrou à définir pour l'enregistrement avec id = 9 car cela n'existe pas et donc il ne peut pas être mis à jour - je ne pense pas que vous pouvez compter sur innodb en définissant un verrou de lecture dans un tel cas. Il devrait cependant définir un verrou d'écriture sur id = 9. Si à un moment ultérieur, l'une des transactions met à jour la table et touche les mêmes données qu'une autre transaction - la mise à jour bloquera probablement l'une des transactions et échouera ultérieurement si l'autre transaction a validé ces données.Il est tout à fait normal que les transactions échouent dans des scénarios comme celui-ci - vous laissant le soin de le gérer - ce qui revient généralement à réessayer la transaction.

S'il y avait un enregistrement avec id = 9, vous verriez probablement le bloc 2. select jusqu'à ce que la première transaction soit terminée, car il y a maintenant un enregistrement qui doit être lu verrouillé si la première transaction décide de mettre à jour cette rangée.

+1

Oui, s'il y a un enregistrement avec id = 9 le 2ème sélectionne les blocs. Alors, comment dois-je "réserver" l'id 9? J'ai ce problème parce que je stocke une relation entre des paires d'objets et je ne veux qu'une seule ligne pour paire donnée d'objets. –

+0

Difficile de dire exactement ce que vous essayez de faire. L'une de vos transactions va-t-elle créer id = 9? Dans ce cas, l'autre transaction échouera/sera annulée. – nos

+0

Je suis en train de mettre en œuvre quelque chose comme ça en Java: (! RowExists (9)) si { createRowWithId (9); } else { return "Erreur"; } Et je reçois IDs dupliqués ... –