Contexte: Le framework utilisé est Spring et toutes les requêtes sont exécutées avec JdbcTemplate. La version de Mysql Server est 5.6.19. Le table
est un InnoDB table
et les valeurs par défaut comme auto commit
et le niveau d'isolement en lecture répétable sont définis.
Problème : un Insert
se produit à l'intérieur d'une transaction et un select
qui lit les mêmes données insérées ne voit pas les données. Les select
courses après le insert
et après la insert
transaction a commited
.
J'ai activé le journal bin ainsi que le journal général dans mysql. Journaux pertinents ci-dessous
bin-log:
SET TIMESTAMP=1438265764/*!*/;
BEGIN
/*!*/;
# at 249935389
#150730 14:16:04 server id 1 end_log_pos 249935606 CRC32 0xa6aca292 Query thread_id=40 exec_time=0 error_code=0
SET TIMESTAMP=1438265764/*!*/;
insert into user_geo_loc_latest(user_id, lat, lng) values(x,y,z) on duplicate key update lat=y, lng=z
/*!*/;
# at 249935606
#150730 14:16:06 server id 1 end_log_pos 249936255 CRC32 0x2a52c734 Query thread_id=40 exec_time=0 error_code=0
SET TIMESTAMP=1438265766/*!*/;
INSERT INTO table(txnid) VALUES ('885851438265675046')
/*!*/;
# at 249936255
#150730 14:16:06 server id 1 end_log_pos 249936514 CRC32 0x6cd85eb5 Query thread_id=40 exec_time=0 error_code=0
SET TIMESTAMP=1438265766/*!*/;
INSERT INTO table2(x) VALUES (y)
/*!*/;
# at 249936514
#150730 14:16:06 server id 1 end_log_pos 249936545 CRC32 0xceb9ec56 Xid = 9406873
COMMIT/*!*/;
Journal des requêtes
150730 14:16:04 40 Query ...
....
40 Query select count(*) from table where txnid = '885851438265675046'
40 Query select @@session.tx_read_only
40 Query INSERT INTO table(txnid) VALUES ('885851438265675046')
40 Query select @@session.tx_read_only
40 Query INSERT INTO table2(x) values(y)
40 Query commit
....
150730 14:16:07 36 Query select pp.*, b.create_date from table pp left join bill b on pp.bill_id = b.bill_id where pp.txnid = '885851438265675046'
Curieusement, First insert
(249935389) ne devrait pas du tout faire partie de la transaction. Il s'agit d'un appel d'API distinct et totalement indépendant. Cela pourrait être le printemps en le mélangeant avec la transaction ou je lis mal le journal? AFAIK puisque c'est sur le même thread, cela implique que l'insert est dans la transaction.
Les deux suivants inserts
font partie de la transaction et il semble que la transaction soit validée. (249936514). Maintenant, la requête de sélection (la dernière dans le journal général) s'exécute après la validation et elle ne voit pas les données. Il renvoie 0 lignes. Comment cela peut-il arriver compte tenu des données committed
? Ou n'est-ce commit
pas sur le fil 40? Puisqu'il n'a pas l'ID de thread.
Pour résumer, j'ai deux questions.
Est-ce que le
BEGIN
dans le binlog est avant leINSERT INTO user_geo_loc
(qui ne fait pas partie de la transaction), est-ce un bogue avec spring / Jdbc ou MySql le fait simplement car il sait que cette transaction a déjà été validée (car les transactions sont écrites dans binlog quand elles ont réussi) et ne serait donc jamais annulé.Étant donné que la validation se produit avant la sélection (la validation est à 14:16:06 et la sélection est à 14:16:07), comment se fait-il que la sélection ne renvoie pas la ligne insérée par la transaction?
C'est extrêmement déroutant. Toute aide serait appréciée
Remarque: Les requêtes dans le bac et le journal des requêtes ont été modifiées pour supprimer les informations sensibles. Mais l'essence des requêtes reste la même
Edit: mis à jour avec le journal général et le journal des requêtes avec un exemple détaillé.
la source
BEGIN
ouSTART TRANSACTION
. Utilisez-vous plutôtautocommit=0
? (Je préfère commencer ... s'engager; cela clarifie l'étendue de la transaction.)Réponses:
J'essaie de faire une hypothèse sur la deuxième question:
Les transactions sont gérées par Spring. Il serait donc possible qu'avant d'exécuter le
select
ressort ait levé unestart transaction
ou qu'il ait déjà utilisé la connexion pour exécuter une autre requête.Je démarre une première session où je simule un insert dans une table
t
:Je crée une nouvelle session, session2, où
autocommit
est définie sur 0. Dans cette nouvelle session, une transaction est implicitement démarrée lors de l'exécution d'une sélection.Passez à session1 pour valider l'insertion.
Maintenant, passez à nouveau à la session2:
Session2 ne peut pas voir la ligne qui vient d'être insérée. Si a
commit
est levé dans la session2, nous pouvons voir une nouvelle ligne insérée dans la session1Le journal général ressemble à ceci:
La première ligne est liée à la session 2. C'est lorsque la session 2 ouvre la transaction.
Je ne sais pas si c'est ce qui se passe dans votre cas. Vous pouvez vérifier dans votre journal général si le connection_id 36 a été utilisé pour d'autres requêtes. Faites-nous savoir.
la source