Maison > base de données > tutoriel mysql > le corps du texte

Exemple d'analyse de dépannage de blocage Mysql

WBOY
Libérer: 2023-05-29 12:22:06
avant
1272 Les gens l'ont consulté

Le problème apparaît

Un après-midi, tout à coup, le système s'est alarmé et a lancé une exception :

Un examen plus approfondi semble être une exception d'annulation de transaction. Il indique qu'elle a été annulée en raison d'un blocage. Comme j'ai encore une certaine compréhension des verrous Mysql, j'ai commencé à enquêter de manière proactive sur ce problème. .

Tout d’abord, recherchez Innodb Status dans la base de données. Les dernières informations de blocage seront enregistrées dans Innodb Status. Entrez la commande suivante :

. AFFICHER L'ÉTAT DU MOTEUR INNODB

Les informations de blocage sont les suivantes, et les informations SQL ont été simplement traitées :

​-----------------------------

DERNIÈRE IMPASSE DÉTECTÉE

​-----------------------------

2019-02-22 15:10:56 0x7eec2f468700

*** (1) TRANSACTION :

TRANSACTION 2660206487, ACTIF 0 sec lecture de l'index de départ

 tables MySQL utilisées 1, verrouillées 1

LOCK WAIT 2 structures de verrouillage, taille de tas 1136, 1 verrou(s) de ligne

ID de thread MySQL 31261312, descripteur de thread du système d'exploitation 139554322093824, identifiant de requête 11624975750 10.23.134.92 erp_crm__6f73 mise à jour

/*id:3637ba36*/UPDATE tenant_config SET

open_card_point = 0

​où tenant_id = 123

*** (1) EN ATTENTE QUE CE VERROU SOIT ACCORDÉ :

RECORD LOCKS identifiant d'espace 1322 numéro de page 534 n bits 960 index uidx_tenant de la table ——erp_crm_member_plan——. ——tenant_config—— trx id 2660206487 lock_mode X verrouille l'enregistrement mais pas d'attente

*** (2) TRANSACTION :

TRANSACTION 2660206486, ACTIF 0 sec lecture de l'index de départ

 tables MySQL utilisées 1, verrouillées 1

​3 structures de verrouillage, taille de tas 1136, verrou(s) à 2 lignes

ID de thread MySQL 31261311, descripteur de thread du système d'exploitation 139552870532864, identifiant de requête 11624975758 10.23.134.92 erp_crm__6f73 mise à jour

/*id:3637ba36*/UPDATE tenant_config SET

open_card_point = 0

​où tenant_id = 123

*** (2) DIT LE(S) SERRURE(S) :

RECORD LOCKS identifiant d'espace 1322 numéro de page 534 n bits 960 index uidx_tenant de la table ——erp_crm_member_plan——. ——tenant_config—— trx id 2660206486 mode de verrouillage S

*** (2) EN ATTENTE QUE CE VERROU SOIT ACCORDÉ :

RECORD LOCKS identifiant d'espace 1322 numéro de page 534 n bits 960 index uidx_tenant de la table ——erp_crm_member_plan——. ——tenant_config—— trx id 2660206486 lock_mode X verrouille l'enregistrement mais pas d'attente

*** NOUS ANNULONS LA TRANSACTION (1)

----------------

Permettez-moi d'analyser et d'expliquer brièvement ce journal de blocage. Lorsque la transaction 1 exécute l'instruction Update, elle doit obtenir l'index uidx_tenant, puis le verrou X (verrouillage de ligne) sur la condition Where. La transaction 2 exécute la même instruction Update et réfléchit également. uidx_tenant. Pour acquérir le verrou X (verrouillage de ligne), un blocage s'est produit et la transaction 1 a été annulée. J'étais très confus à ce moment-là, et j'ai rappelé les conditions nécessaires pour qu'une impasse se produise :

Mutuellement exclusifs.

Conditions de demande et de retenue.

Aucune privation de conditions.

Cycle d'attente. D'après le journal, on peut voir que les transactions 1 et 2 sont en concurrence pour le verrou de ligne de la même ligne. C'est un peu différent de la concurrence cyclique précédente pour les verrous. Peu importe la façon dont vous le regardez, elles ne peuvent pas satisfaire. la condition d’attente circulaire. Après avoir été rappelé par des collègues, étant donné que le journal des blocages ne peut pas être étudié, le problème ne peut être étudié qu'à partir du code métier et des journaux métier. La logique de ce code est la suivante :

public int saveTenantConfig(PoiContext poiContext, TenantConfigDO tenantConfig) {

​essayez {

​retourner tenantConfigMapper.saveTenantConfig(poiContext.getTenantId(), poiContext.getPoiId(), tenantConfig);

} catch (DuplicateKeyException e) {

​LOGGER.warn("[saveTenantConfig] conflit de clé primaire, mettre à jour l'enregistrement. context:{}, config:{}", poiContext, tenantConfig);

​retourner tenantConfigMapper.updateTenantConfig(poiContext.getTenantId(), tenantConfig);

}

}

La signification de ce code est de sauvegarder un fichier de configuration. Si un conflit d'index unique survient, il sera mis à jour. Bien sûr, l'écriture ici n'est peut-être pas très standardisée, mais elle peut réellement être utilisée

. insérer dans …

sur la mise à jour de la clé en double

Le même effet peut être obtenu, mais même si cela est utilisé, une impasse se produira en réalité. Après avoir lu le code, mon collègue m'a envoyé le journal d'activité à ce moment-là,

Vous pouvez voir que trois journaux se sont produits en même temps, indiquant qu'un conflit d'index unique s'est produit et a entré l'instruction mise à jour, puis qu'un blocage s'est produit. À ce stade, la réponse semble enfin un peu plus claire.

À ce stade, regardons la structure de notre table comme suit (simplifiée) :

CRÉER UNE TABLE ——tenant_config——

​——id—— bigint(21) PAS NULL AUTO_INCREMENT,

——tenant_id—— int (11) NON NULL,

——open_card_point—— int (11) PAR DÉFAUT NULL,

CLÉ PRIMAIRE (——id——),

​CLÉ UNIQUE ——uidx_tenant—— (——tenant_id——)

) MOTEUR=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT

Notre tenant_id est utilisé comme index unique, et notre insertion et mise à jour où les conditions sont toutes basées sur l'index unique.

MISE À JOUR tenant_config SET

open_card_point = 0

​où tenant_id = 123

À ce stade, je pense que le verrouillage de l'index unique lors de l'insertion est lié. Ensuite, nous procéderons à une analyse approfondie à l'étape suivante.

Analyse approfondie

Ci-dessus, nous avons dit que trois transactions entrent dans l'instruction de mise à jour. Pour simplifier l'explication, nous n'avons besoin que de deux transactions pour saisir l'instruction de mise à jour en même temps. Le tableau suivant montre l'ensemble de notre processus :

. Astuce : le verrou S est un verrou partagé, le verrou X est un verrou d'exclusion mutuelle. De manière générale, les verrous X, les verrous S et les verrous X s'excluent mutuellement, mais les verrous S et les verrous S ne s'excluent pas mutuellement.

D'après le processus ci-dessus, nous voyons que la clé de cette impasse est d'acquérir le verrou S. Pourquoi devons-nous acquérir le verrou S lors d'une nouvelle insertion ? Parce que nous devons détecter un index unique ? Sous le niveau d'isolation RR, si vous souhaitez lire, il s'agit de la lecture actuelle, vous devez donc en fait ajouter un verrou S. On constate ici que la clé unique existe déjà. A ce moment, l'exécution de la mise à jour sera bloquée par les verrous S des deux transactions, formant ainsi la condition d'attente de boucle ci-dessus.

Conseils : Dans MVCC, la différence entre la lecture actuelle et la lecture d'instantané : la lecture actuelle doit être verrouillée à chaque fois (vous pouvez utiliser le verrouillage partagé ou le verrouillage mutex) pour obtenir les dernières données, tandis que la lecture d'instantané lit le début de cette transaction à ce moment-là, l'instantané a été implémenté via le journal d'annulation.

C'est la raison de tout le blocage. Une autre situation dans laquelle ce type de blocage peut se produire est qu'il y a trois opérations d'insertion en même temps. Si la transaction insérée en premier est finalement annulée, cela se produira également pour les deux autres. transactions.

Solution

Le principal problème ici est de se débarrasser du verrou S. Voici trois solutions pour référence :

. Réduisez le niveau d’isolement RR au niveau d’isolement RC. Ici, le niveau d'isolation RC utilisera la lecture d'instantanés, donc aucun verrou S ne sera ajouté.

Lors de l'insertion à nouveau, utilisez select * for update pour ajouter le verrou X, afin que le verrou S ne soit pas ajouté.

Vous pouvez ajouter des verrous distribués à l'avance, vous pouvez utiliser Redis ou ZK, etc. Pour les verrous distribués, veuillez vous référer à mon article. Parlons des verrous distribués

La première méthode n’est pas très réaliste, car le niveau d’isolement n’est pas facilement modifiable. La troisième méthode est plus problématique. C’est donc la deuxième méthode que nous avons finalement retenue.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal