2010-11-12 9 views
0

Je reçois l'erreur suivante assez souvent lors de l'exécution d'une certaine requête dans ma base de données (toutes les tables utilisent le moteur de stockage InnoDB): "Deadlock trouvé en essayant de verrouiller, essayez de redémarrer la transaction"Deadlock trouvé dans MySQL (InnoDB)

la requête est DELETE FROM sessions WHERE userid != 0 AND lastactivity < 1289594761 AND admin = 1 AND userid NOT IN (SELECT userid FROM users WHERE (userflags & 1048576))

les erreurs ont commencé à se produire lorsque j'ai ajouté le PAS en partie à ma clause WHERE. Pourquoi cela pose-t-il des problèmes et que puis-je faire pour éviter cela?

+0

Alors que vous pourriez être en mesure de minimiser les blocages, voir http://stackoverflow.com/questions/2596005/working-around-mysql-error-deadlock-found-when-trying-to -get-lock-try-restartin – nos

Répondre

2

Une solution simple serait de séparer cela en deux requêtes consécutives. I.e.,:

SELECT utilisateur dans des utilisateurs #tmptable FROM WHERE (userflags & 1048576); SUPPRIMER à partir des sessions O WH ID utilisateur! = 0 ET lastactivity < 1289594761 AND admin = 1 AND id_utilisateur NOT IN (sélectionnez userid dans #tmptable);

De cette façon, vous travaillez avec une copie de session locale des valeurs de la deuxième table et ne provoque pas de verrouillage de lecture sur celle-ci. Cependant, c'est juste une solution rapide et sale. Une meilleure solution consisterait à analyser les paramètres de verrouillage des transactions de toutes les activités qui touchent ces deux tables et à réécrire la requête, si vous la réutilisez régulièrement.

+0

Il est juste utilisé dans un cronjob de maintenance, donc s'il échoue de temps en temps ce n'est pas une grande chose - je les ai ignorés pendant quelques mois mais ayant pour supprimer les messages de notification d'erreur devient ennuyeux. ;) – ThiefMaster

+0

Btw, je l'ai fait semblable à ce que vous avez suggéré - ne pas utiliser une table temporaire, mais simplement stocker la liste des utilisateurs dans mon code PHP et ensuite utiliser 'IN (list, of, userids)' dans l'instruction DELETE . – ThiefMaster

2

Probablement vous obtenez l'erreur plus souvent parce que c'est maintenant une requête beaucoup plus lente.

L'opération & sur userflags rend la sous-requête indisponible. Les mots indicateurs ne sont généralement pas une bonne conception de schéma, car ils nécessitent un calcul qui vainc l'indexation. Si vous faites beaucoup de requêtes de test de bits, des colonnes séparées de petits types de données (par ex. TINYINT) peuvent être meilleures.

Si votre schéma fonctionne comme il semble que cela pourrait, vous devriez être en mesure de le faire en utilisant un simple JOIN, qui effectue généralement mieux que les sous-requêtes:

DELETE sessions 
FROM sessions 
JOIN users ON users.userid=sessions.userid 
WHERE sessions.lastactivity<1289594761 AND admin=1 
AND (users.userflags&1048576)=0 

(rejoint sur DELETE est un non -AnSI SQL extension dans MySQL.)

+0

Je suppose que vous vouliez dire 'AND NOT (users.userflags & 1048579)' - mais belle suggestion, je n'ai pas pensé à utiliser JOINs car IMO utilisant JOINs dans une instruction DELETE/UPDATE rend la requête étrange – ThiefMaster

+0

Ah, oui , vous avez raison-ajouté. – bobince