当前位置:网站首页>Auto - vérification recommandée! Les bogues MySQL ne font pas reculer les transactions, peut - être êtes - vous à risque!

Auto - vérification recommandée! Les bogues MySQL ne font pas reculer les transactions, peut - être êtes - vous à risque!

2022-06-23 14:29:00 Canal de programme DD

cfc88d26b1077d8566245effbeea22c4.png

Auteur:KLBlogueurs

Source::https://my.oschina.net/klblog/blog/5542934

Sur la question de la non - rétrocession des transactions,On en a déjà parlé.:

Partager unOpen Source Document Online Preview Project SolutionkkFileViewUne mise à jour de l'auteur.

Comme le titre,Le problème a finalement été identifié parce que mysql-connector-java:8.0.28 Un bug Résultat.Mais avant que la vérité n'apparaisse,,Toute la question est confuse.,Le blogueur n'a pas vérifié si fort depuis longtemps. bug ,Avec des couches debug En profondeur,La vérité est apparue..C'est une question de bas niveau. jdbc Problèmes de conduite,Universel,Peut - être inconsciemment.,Votre application souffre également de cela en ligne bug De la destruction,Alors..., S'il vous plaît, écoutez - moi bien. , Puis retournez vérifier l'état de votre application , Est - ce que j'ai aussi marché sur la fosse . Comme direct peut être tiré directement à la fin de l'article pour voir le résultat .

Contexte

La narration commence généralement par les personnages 、Contexte. Il n'y a pas d'exception ici , Présentez d'abord les parties intéressées. .En général, Plus l'histoire est riche, plus elle est merveilleuse. , Mais ici, les blogueurs pensent à l'espace (Pas de conneries.) Les détails qui n'ont rien à voir avec l'orientation des résultats seront ignorés , Essaie d'être complet. .

  • commons-db : Notre entretien interne , Un moteur d'annotation Spring Composantes de la gestion des sources de données multiples dans un contexte écologique . Composants pour chaque DataSource Par défaut avec quelques optimisations de performance , Pas tout listé , Mais contient des attributs qui influencent l'orientation du problème (useLocalSessionState),Comme suit:

Properties defaultProperties = new Properties();
defaultProperties.put("prepStmtCacheSize", 300);
defaultProperties.put("prepStmtCacheSqlLimit", 2048);
defaultProperties.put("useLocalSessionState", true);
defaultProperties.put("cacheResultSetMetadata", true);
defaultProperties.put("elideSetAutoCommits", true);
  • java-project : Éléments utilisés pour tester la fonctionnalité des composants , Les tests de comportement sont comparés aux éléments problématiques .spring-boot:2.5.4、mysql-connector-java:8.0.26

  • store: Projet de bibliothèque de jeux , C'est ce projet qui a trouvé le problème. .spring-boot:2.6.6 、mysql-connector-java:8.0.28

  • Alicloud RDS (MySQL): Alicloud MySQL Le niveau d'isolement par défaut est READ_COMMITTED,Et MySQL Le niveau d'isolement par défaut est REPEATABLE_READ

Description:java-project Et store De commons-db Les versions sont différentes , Parce que ça n'affecte pas les résultats . Supposons qu'ils soient identiques. .

Questions

Un jour., Développer la rétroaction ,In store Utilisé dans le projet commons-db Composants, Il y a eu un problème avec le report de transaction non efficace .Comme le montre le code ci - dessous:

@Transactional
@DataSource(type = Type.MASTER,value = "developer")
public void addUser(ApolloUser user){
    userRepository.save(user);
    int i = 1/0; //Lancer une exception
}
  • En particulier::Mise en œuvre addUser Méthodes,Quand 1/0 Jetez RuntimeException Lorsque le type est anormal ,user Objet ajouté avec succès .En résumé,【 Le ROLLBACK des transactions n'est pas valide 】.

Hypothèses

  • Hypothèses 1: J'ai supposé que c'était @Transactional De aop Ça n'a pas marché, Provoque une transaction explicite non ouverte .

  • Hypothèses 1 C'est faux., Parce que c'est ouvert debug Après le mode log , Sortie claire du Journal de comportement pour chaque étape de la transaction ,Par exemple::

a4f1f78b2ca4a976f85ba354fff6d26b.png
img
  • Hypothèses 2: Compte tenu de l'utilisation commons-db , Si vous avez des problèmes de gestion de la connectivité au niveau du cadre , Provoque l'ouverture de la transaction 、 La connexion obtenue lors du retournement de transaction est incohérente , Ça pourrait aussi causer ce problème. .

  • Hypothèses 2 C'est faux.: Pas tout de suite. , Parce que le journal ci - dessus montre que la connexion est la même . Et différentes connexions s'ouvrent de façon inattendue 、 L'opération de report d'une transaction devrait lancer une exception. .

Alors viens ici., La question est dans l'impasse . Je ne peux m'empêcher de réfléchir. , Un code qui semble inoffensif pour les humains et les animaux. , Un journal des transactions apparemment logique , Pourquoi la transaction a - t - elle échoué? ?????

Transfert

Transfert 1

Par la suite,Je suis java-project Dans le projet,Utiliser le même MySQL Testé., La transaction a été reportée avec succès . Indique que le problème n'affecte que des environnements spécifiques , Et on peut trouver des problèmes en comparant les différences entre les deux projets , Plus près de la vérité .

Transfert 2

Un autre message clé est venu du côté du développement. ,In store Dans le projet, Lorsque le niveau d'isolement est défini à REPEATABLE_READ Heure, Le ROLLBACK de transaction est en vigueur .Code comme:

@Transactional(isolation = Isolation.REPEATABLE_READ)
   @DataSource(type = Type.MASTER,value = "developer")
   public void addUser(ApolloUser user){
       userRepository.save(user);
       int i = 1/0;
   }

Par ici., Mais est - ce une question de niveau d'isolement? ?Apparemment pas., Parce que dans le dictionnaire cognitif des transactions, , Il n'y a pas eu de mot où le niveau d'isolement a affecté le retour en arrière de la transaction .Et de java-project Les tests de ,Dans le même RC Niveau d'isolement inférieur,java-project Ça peut marcher..

La première solution

Et c'est un pas en avant. , Peut être résolu temporairement en fixant un niveau d'isolement 【 Problème d'inefficacité du report des transactions 】.Mais,Différents niveaux d'isolement, Verrouillage des transactions 、 Les performances simultanées sont différentes , Il faut s'y attendre avant de procéder à un ajustement. .

Transfert 3

Si quelque chose tourne mal, il doit y avoir un démon., Dans l'esprit de l'incrédulité, c'est le niveau d'isolement qui cause le problème. ,Je suis store Dans le cadre du projet  isolation Set to Isolation.READ_UNCOMMITTED , Le ROLLBACK des transactions est également en vigueur . Cela signifie également qu'il n'y a pas de relation directe avec le niveau d'isolement . Et ensuite, dans l'exploration 【 Pourquoi par défaut READ_COMMITTED Faire en sorte que la transaction ne prenne pas effet?】 J'ai vérifié , J'ai trouvé des problèmes , Le code suivant fait partie de la logique de transaction (Code source voir:DataSourceUtils.prepareConnectionForTransaction ()):

e54b6a9e9e63a14b059ac97b05a35c9e.png
img

Découverte,Comparé à RR、RU , La différence est quand le niveau d'isolement est READ_COMMITTED Heure, - Non. session Il y a une opération de mise à jour . Ce n'est qu'un phénomène de plus , Peut expliquer le comportement après avoir appris la vérité , N'a pas atteint le bord de la vérité .

Analyse

Tout ce qui précède , Pas de vrai problème. . Alors ne fais pas d'autres tests. , D'abord, il y a des attentes. , Dans la validation ciblée .

Commençons par la normale générale. Spring Transactional Processus complet de report des transactions , Ce qui est courant, c'est qu'il n'y a pas de configuration spéciale des paramètres , En général, ces paramètres ne sont pas configurés .

  • 1、Ajouté @Transactional Avant l'exécution de la méthode , Le gestionnaire de transaction sera exécuté (DataSourceTransactionManager)De doBegin Méthode créer une transaction,In doBegin Méthode,Va mettre en place autoCommit = false. Détermine si le niveau d'isolement actuel est conforme à la définition de l'utilisateur , Sinon, le niveau d'isolement est mis à jour .

3caedc85784b8c4df05452d161cafb62.png
img
  • 2、 Après l'échec de l'exécution de la méthode , Le gestionnaire de transaction sera exécuté (DataSourceTransactionManager)De doRollback Méthode ROLLBACK transaction.

De Spring Transactional Le Journal des transactions de ,Créer une transaction、Mise en place d'une transaction manuelle、 Toutes les transactions de ROLLBACK ont une impression de journal . Alors nous allons aller plus loin dans la couche d'entraînement 、 Ou prendre un sac. , Si toutes ces instructions sont envoyées à MySQL Server C'est.

Problèmes de positionnement

Comme l'analyse,In store Dans le projet,Tapez le point d'arrêt à mysql-connector-java Conduite NativeSession.execSQL () Méthode,Et MySQL Server Toutes les instructions d'interaction , Cette méthode est finalement appelée à exécuter .J'ai trouvé le problème:

  • Lorsque la transaction a échoué , Le processus de transaction n'a pas été exécuté SET autocommit=0 Directives.

égal à dire que la transaction a échoué , Les transactions ont toujours été soumises automatiquement ,Alors..., Une opération de ROLLBACK anormale ne fait pas reculer les données qui ont été persistantes .

Après avoir découvert ce problème, Ensuite, localisez pourquoi Spring Mise en œuvre Set autoCommit=false , Questions qui n'ont finalement pas été mises en œuvre ,Encore une fois【Transfert 1】De java-project Comparaison de la mise en service en une seule étape du projet ,J'ai trouvé un code clé(ConnectionImpl.setAutoCommit ()) Code incohérent dans les deux projets :

java-project,mysql-connector-java:8.0.26( Le ROLLBACK des transactions prend effet )

a7191cabea3c5e23188ed1d6592c260b.png
img

store,mysql-connector-java:8.0.28( Le ROLLBACK de transaction n'est pas valide )

5c7073d8f8897239edf3507e6f28fa3c.png
img

Voici un aperçu de ce paramètre

  • useLocalSessionState: Entretien local sessionState , Au besoin de jugement 【 Mode de soumission des transactions 】、【Niveau d'isolement】Lors du réglage, Obtenir l'état local , Au lieu d'être comme MySQL Server Demander.

Ce paramètre aide à réduire et MySQL L'interaction de, Améliore les performances d'écriture des données . Ainsi, lors de l'optimisation des performances des paramètres , Par défaut à true C'est.Ici.,Si useLocalSessionState=false, C'est ce qui va masquer ça. bug.

Décrypter

Parce que store,mysql-connector-java:8.0.28 La version en question isAutocommit () Logique comportementale et isAutoCommit () Incohérence, J'aurais dû appeler le juge isAutocommit Retour true Heure,Et il est revenu false. C'est ce qui a mené à store Reçu Spring Transactional Paramètres autoCommit=false Sur demande,Parce que needsSetOnServer=false , Sauter le vrai lancement Set autocommit=0 Mise en œuvre de la directive. Fait en sorte que le mode de transaction actuel soit le mode d'engagement automatique , Donc quand il y a des ajouts, des suppressions et des modifications dans la transaction , Dès que l'exécution sera terminée commit Persistance. Dans ce cas, la transaction est lancée en cas d'exception rollback , Naturellement, il n'y a pas de retour en arrière d'une transaction qui a déjà été engagée automatiquement . C'est une bonne explication que le Journal des transactions affiché au début est complet , Mais la transaction est un problème de retour en arrière inefficace .

Deuxième solution

J'ai vérifié ici., La deuxième solution est venue. , Il suffit de laisser le jugement s'exécuter Set autocommit=0 Heure needsSetOnServer=true Ça suffit. .Alors...,Tant que c'est vrai store Appliquer l'un ou l'autre des deux paramètres suivants pour le réglage de la configuration des paramètres , Ça résoudra le problème. . C'est mieux que la première. :

useLocalSessionState=false
auto-commit=false

Pourquoi? isolation Set to Isolation.REPEATABLE_READ Ça marchera.

Alors c'est fini ici? ?Non., On s'attend à ce que même useLocalSessionState=ture , La transaction devrait aussi être complète .Et n'oublie pas isAutoCommit () Et isAutocommit () Différences. Commençons par leur définition. :

public boolean isAutocommit() {
  return (this.statusFlags & 2) != 0;
}
 
public boolean isAutoCommit() {
  return this.autoCommit;
}

C'est vrai. mysql-connector-java:8.0.28 Drive in,Utiliser statusFlags État remplacé autoCommit Identification( On ne se demande pas pourquoi on a fait ce changement. ), Voilà l'explication.

  • Transfert 2: Lorsque le niveau d'isolement est défini à REPEATABLE_READ Heure, Le ROLLBACK de transaction est en vigueur . Parce que lorsque le niveau d'isolement défini par l'utilisateur RR Et par défaut RC En cas d'incohérence,Ça va déclencher session Définir un nouveau niveau d'isolement ,À ce stade statusFlags = 0 Mise à jour vers statusFlags = 2. Donc C'est un appel isAutocommit () Retour true , Mise en œuvre satisfaisante SET autocommit=0 Conditions de la directive .

Je sais pourquoi. , Et je sais exactement isAutoCommit () != isAutocommit () , Mais il n'est pas clair pourquoi cela a été fait. . Les questions spécifiques ne sont pas énumérées ici pour le moment. ,Revenons à la question.

Question de la récurrence

Maintenant que le problème est bien réglé, , Ensuite, suivez le processus de dépannage normal , Réapparaître comme prévu , Définir les limites du problème . Parce qu'il peut y avoir d'autres facteurs d'influence qui peuvent causer des problèmes ensemble .In java-project Dans le projet, Effectuer les ajustements de version suivants pour les dépendances

  • Mise à jour spring-boot:2.6.6 Version et store Soyez cohérent: Le problème revient.

  • Tiens bon. spring-boot:2.5.4,Ajustement mysql-connector-java:8.0.28 : Le problème est revenu

Par ici., C'est pratiquement exclu. Spring Transactional C'est suspect. . Et il a verrouillé la lance. mysql-connector-java:8.0.28 Sur le corps..

Confirmation bug

Compte tenu du fait que mysql-connector-java:8.0.26 De isAutoCommit A changé mysql-connector-java:8.0.28 De isAutocommit Il doit y avoir une raison, Avec l'intention de clarifier l'auteur du Code pour soumettre ce changement , Je l'ai renversé. github.

  • https://github.com/mysql/mysql-connector-j

J'ai trouvé. github Documents soumis pour  commit ,Découverte, La dernière version a été modifiée. isAutoCommit () C'est,Et puis Commit Message Il est clair que c'est 8.0.28 Version bug,Par exemple:.

07784f2f58613bc680b78f1fd4750b05.png
img

Jusqu'ici., Enfin, la vérité s'est révélée .

Réparation

  • 8.0.29 release:https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/news-8-0-29.html

  • A connection did not maintain the correct autocommit state when it was used in a pool with useLocalSessionState=true. (Bug #106435, Bug #33850099)

Solution finale

Par exemple: 8.0.29 release Notes d'annonce ,Réparé. 8.0.28 Dans la configuration useLocalSessionState=true Dans le cas de,autoCommit Problème avec le réglage de l'état .Alors..., Mise à jour de l'application vers mysql-connector-java:8.0.29 La version est OK.

Conclusion

Résumez d'abord le tableau des questions comme suit: Spring Transactional【 Le ROLLBACK de transaction n'est pas valide , Les données soumises avant le ROLLBACK ne sont pas rollback 】,La raison fondamentale est 【mysql-connector-java:8.0.28 Une modification soumise par la version bug , Provoque l'activation useLocalSessionState=true Dans le cas de,autoCommit Il y a un problème avec le réglage de l'état 】.

Et parce que spring-boot:2.6.3 ~ 2.6.7 , Par défaut pour ces cinq versions MySQL L'entraînement est mysql-connector-java:8.0.28 ,Et useLocalSessionState=true Presque. Java JDBC DataSource Standard in,Donc ça bug On estime qu'une vague de personnes . Et parce que ça n'affecte que l'opération de rollback , C'est pourquoi cette question est très cachée. , Pas facile à détecter , Ce qu'on appelle l'influence profonde .

Enfin, Envoyer cet article prend en charge l'auteur , En même temps, faire connaître le problème à un plus grand nombre de petits partenaires et le traiter à l'avance , Évitez l'embarras d'être appelé au milieu de la nuit

Nous avons créé un groupe d'échanges techniques de haute qualité,Avec les bons,Moi aussi, je serai meilleur,Dépêchez - vous.Cliquez sur Ajouter un groupe,Profitez du bonheur de grandir ensemble.En plus,Si tu veux changer d'emploi récemment,J'ai dépensé2Une grande vague de sutras a été recueillie au cours de la semaine,Celui qui est prêt à changer d'emploi après le festival peutCliquez ici pour recevoir

Lectures recommandées

··································

Bonjour,Je suis un singe programmé.DD,10Développer de vieux conducteurs、AlicloudMVP、Tencent CloudTVP、J'ai fait des livres et des affaires、Entreprises publiques4Année Internet6Année.Du développement ordinaire à l'architecte、Aux partenaires.Tout le chemin,Ce qui m'a le plus impressionné, c'est de continuer à apprendre et à me concentrer sur les frontières.Tant que tu peux tenir,Pense plus、Ne te plains pas.、Fais - le souvent,C'est facile de dépasser les virages!Alors...,Ne me demandez pas si je peux faire quelque chose maintenant.Si vous regardez une chose,J'ai dû insister pour voir l'espoir,Au lieu de voir l'espoir persister.Fais - moi confiance.,Tiens bon,Tu dois être meilleur que maintenant!Si vous n'avez pas encore de direction,Je peux d'abord me concentrer sur moi,Voici souvent des informations de pointe,Pour vous aider à accumuler des capitaux pour le dépassement de virage.

Cliquez pour recevoir2022Mise à jour10000TMatériel d'apprentissage

原网站

版权声明
本文为[Canal de programme DD]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/174/202206231338592507.html