J'ai une table innoDB qui enregistre les utilisateurs en ligne. Il est mis à jour à chaque actualisation de page par un utilisateur pour garder une trace des pages sur lesquelles il se trouve et de sa dernière date d'accès au site. J'ai ensuite un cron qui s'exécute toutes les 15 minutes pour SUPPRIMER les anciens enregistrements.
J'ai trouvé un «Deadlock» en essayant de verrouiller; essayez de redémarrer la transaction 'pendant environ 5 minutes la nuit dernière et cela semble être le cas lors de l'exécution d'INSERT dans ce tableau. Quelqu'un peut-il suggérer comment éviter cette erreur?
=== EDIT ===
Voici les requêtes en cours d'exécution:
Première visite sur le site:
INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
Sur chaque page, actualisez:
UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888
Cron toutes les 15 minutes:
DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND
Il effectue ensuite quelques décomptes pour enregistrer certaines statistiques (par exemple: membres en ligne, visiteurs en ligne).
Réponses:
Une astuce simple qui peut aider avec la plupart des blocages est de trier les opérations dans un ordre spécifique.
Vous obtenez un blocage lorsque deux transactions tentent de verrouiller deux verrous dans des ordres opposés, c'est-à-dire:
Si les deux s'exécutent en même temps, la connexion 1 verrouille la clé (1), la connexion 2 verrouille la clé (2) et chaque connexion attend que l'autre relâche la clé -> blocage.
Maintenant, si vous avez modifié vos requêtes de telle sorte que les connexions verrouillent les clés dans le même ordre, c'est-à-dire:
il sera impossible d'obtenir une impasse.
Voici donc ce que je propose:
Assurez-vous qu'aucune autre requête ne verrouille l'accès à plusieurs clés à la fois, à l'exception de l'instruction delete. si vous le faites (et je soupçonne que vous le faites), commandez leur OERE en (k1, k2, .. kn) dans l'ordre croissant.
Corrigez votre instruction de suppression pour qu'elle fonctionne dans l'ordre croissant:
Changement
À
Une autre chose à garder à l'esprit est que la documentation mysql suggère qu'en cas de blocage, le client devrait réessayer automatiquement. vous pouvez ajouter cette logique à votre code client. (Disons, 3 tentatives sur cette erreur particulière avant d'abandonner).
la source
Le blocage se produit lorsque deux transactions attendent l'une de l'autre pour acquérir un verrou. Exemple:
Il existe de nombreuses questions et réponses sur les blocages. Chaque fois que vous insérez / mettez à jour / supprimez une ligne, un verrou est acquis. Pour éviter un blocage, vous devez ensuite vous assurer que les transactions simultanées ne mettent pas à jour la ligne dans un ordre qui pourrait entraîner un blocage. De manière générale, essayez d'acquérir le verrou toujours dans le même ordre, même dans une transaction différente (par exemple toujours la table A d'abord, puis la table B).
Une autre raison de blocage dans la base de données peut être l' absence d'index . Lorsqu'une ligne est insérée / mise à jour / suppression, la base de données doit vérifier les contraintes relationnelles, c'est-à-dire s'assurer que les relations sont cohérentes. Pour ce faire, la base de données doit vérifier les clés étrangères dans les tables associées. Cela peut entraîner l'acquisition d'un autre verrou que la ligne modifiée. Assurez-vous alors de toujours avoir l'index sur les clés étrangères (et bien sûr les clés primaires), sinon cela pourrait entraîner un verrou de table au lieu d'un verrou de ligne . Si le verrouillage de table se produit, le conflit de verrouillage est plus élevé et la probabilité de blocage augmente.
la source
Il est probable que l'instruction delete affectera une grande partie du nombre total de lignes du tableau. Finalement, cela pourrait entraîner l'acquisition d'un verrou de table lors de la suppression. Conserver un verrou (dans ce cas, des verrous de ligne ou de page) et acquérir plus de verrous est toujours un risque de blocage. Cependant, je ne peux pas expliquer pourquoi l'instruction d'insertion conduit à une escalade de verrouillage - cela peut avoir à voir avec le fractionnement / l'ajout de pages, mais quelqu'un qui connaît mieux MySQL devra y remplir.
Pour commencer, il peut être utile d'essayer d'acquérir explicitement un verrou de table immédiatement pour l'instruction delete. Consultez les sections VERROUILLER LES TABLEAUX et Verrouiller la table .
la source
Vous pouvez essayer de faire fonctionner ce
delete
travail en insérant d'abord la clé de chaque ligne à supprimer dans une table temporaire comme ce pseudocodeLe briser de cette façon est moins efficace, mais il évite d'avoir à maintenir un verrou de plage de clés pendant le
delete
.Modifiez également vos
select
requêtes pour ajouter unewhere
clause excluant les lignes de plus de 900 secondes. Cela évite la dépendance à l'égard du travail cron et vous permet de le replanifier pour qu'il s'exécute moins souvent.Théorie sur les blocages: je n'ai pas beaucoup d'expérience dans MySQL mais voilà ... Le
delete
va contenir un verrou de plage de clés pour datetime, pour empêcher l'where
ajout de lignes correspondant à sa clause au milieu de la transaction , et lorsqu'il trouvera des lignes à supprimer, il tentera d'acquérir un verrou sur chaque page qu'il modifie. Leinsert
va acquérir un verrou sur la page dans laquelle il est inséré, puis tenter d'acquérir le verrouillage des touches. Normalement, leinsert
patient attendra patiemment que ce verrou de clé s'ouvre, mais cela bloquera si ledelete
tente de verrouiller la même page qu'ilinsert
utilise, car il adelete
besoin de ce verrouillage de page et ainsert
besoin de ce verrouillage de clé. Cela ne semble pas correct pour les insertions, ledelete
etinsert
utilisent des plages de datetime qui ne se chevauchent pas, alors peut-être que quelque chose d'autre se passe.http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html
la source
Dans le cas où quelqu'un est toujours aux prises avec ce problème:
J'ai rencontré un problème similaire où 2 demandes frappaient le serveur en même temps. Il n'y avait aucune situation comme ci-dessous:
Donc, j'étais perplexe pourquoi l'impasse se produit.
Ensuite, j'ai trouvé qu'il y avait une relation parent-enfant entre 2 tables à cause de la clé étrangère. Lorsque j'insérais un enregistrement dans la table enfant, la transaction acquérait un verrou sur la ligne de la table parent. Immédiatement après cela, j'essayais de mettre à jour la ligne parent qui déclenchait l'élévation du verrou à EXCLUSIVE. Comme la 2e transaction simultanée détenait déjà un verrou PARTAGÉ, elle provoquait un blocage.
Reportez-vous à: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html
la source
Pour les programmeurs Java utilisant Spring, j'ai évité ce problème en utilisant un aspect AOP qui réessaye automatiquement les transactions qui s'exécutent dans des blocages temporaires.
Voir @RetryTransaction Javadoc pour plus d'informations.
la source
J'ai une méthode, dont les éléments internes sont enveloppés dans une MySqlTransaction.
Le problème de blocage est apparu pour moi lorsque j'ai exécuté la même méthode en parallèle avec lui-même.
Il n'y a eu aucun problème lors de l'exécution d'une seule instance de la méthode.
Lorsque j'ai supprimé MySqlTransaction, j'ai pu exécuter la méthode en parallèle avec elle-même sans aucun problème.
Partageant mon expérience, je ne préconise rien.
la source
cron
est dangereux. Si une instance de cron ne parvient pas à se terminer avant la suivante, ils sont susceptibles de se battre.Il serait préférable d'avoir un travail en cours d'exécution qui supprimerait certaines lignes, en mettrait en veille, puis recommencerait.
Est également
INDEX(datetime)
très important pour éviter les blocages.Mais, si le test datetime inclut plus de, disons, 20% de la table, le
DELETE
fera une analyse de table. Les petits morceaux supprimés plus souvent constituent une solution de contournement.Une autre raison d'aller avec des morceaux plus petits est de verrouiller moins de lignes.
Conclusion:
INDEX(datetime)
Autres techniques de suppression: http://mysql.rjweb.org/doc.php/deletebig
la source