Lorsque j'ai essayé de sauvegarder un enregistrement en utilisant Model.save () de Django, j'ai rencontré une erreur de blocage MySQL, alors j'ai recherché la raison.
Django1.8.4, MySQL5.5 InnoDB, le niveau d'isolation de transaction est REPEATABLE-READ.
Citez https://docs.djangoproject.com/en/1.8/ref/models/instances/#how-django-knows-to-update-vs-insert.
You may have noticed Django database objects use the same save() method for creating and changing objects. Django abstracts the need to use INSERT or UPDATE SQL statements. Specifically, when you call save(), Django follows this algorithm:
- If the object’s primary key attribute is set to a value that evaluates to True (i.e., a value other than None or the empty string), Django executes an UPDATE.
- If the object’s primary key attribute is not set or if the UPDATE didn’t update anything, Django executes an INSERT.
Django semble rendre l'utilisateur ignorant de INSERT ou UPDATE lors de l'utilisation de save (). Émettez un UPDATE si la clé primaire de l'instance peut être évaluée comme True. Il semble que INSERT est émis à nouveau lorsque la ligne modifiée est 0 dans cette UPDATE.
Préparez le modèle suivant. Derived est un modèle qui hérite de Base.
models.py
from django.db import models
class Base(models.Model):
a = models.IntegerField()
class Derived(Base):
b = models.IntegerField()
Et si vous essayez de générer un enregistrement avec Derived en utilisant Model.save ()
? La réponse est que si vous effectuez le processus de génération d'enregistrement simple suivant en parallèle à haute fréquence, vous obtiendrez une erreur de blocage MySQL ʻERROR 1213 (40001): Blocage trouvé lors de la tentative de verrouillage; essayez de redémarrer la transaction`. Je vais.
create_derived.py
d = Derived(a=1, b=2)
d.save()
=> ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction !!!!
Tout d'abord, vérifiez la définition de table de model.py
. L'instruction CREATE TABLE ressemble à ceci: Le modèle dérivé contient base_ptr_id
comme informations de source d'héritage et est une clé PRIMAIRE.
CREATE TABLE `ham_base` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) NOT NULL,
PRIMARY KEY (`id`)
)
CREATE TABLE `ham_derived` (
`base_ptr_id` int(11) NOT NULL,
`b` int(11) NOT NULL,
PRIMARY KEY (`base_ptr_id`),
CONSTRAINT `ham_derived_base_ptr_id_12f18f813c81ff4f_fk_ham_base_id` FOREIGN KEY (`base_ptr_id`) REFERENCES `ham_base` (`id`)
)
Ensuite, vérifiez la requête émise à save ()
de create_derived.py
.
INSERT INTO `ham_base` (`a`) VALUES (1);
UPDATE `ham_derived` SET `b` = 2 WHERE `ham_derived`.`base_ptr_id` = 1 ; args=(2, 1)
INSERT INTO `ham_derived` (`base_ptr_id`, `b`) VALUES (1, 2); args=(1, 2)
base_ptr_id
a été confirmé par le premier INSERT et que la PRIMARY KEY a été évaluée comme True. Cependant, puisqu'il n'y a aucun enregistrement dérivé à ce stade, UPDATE est toujours manquée.
――Enfin, depuis que UPDATE a secoué, je crée un enregistrement dérivé avec INSERT.Il semble que la deuxième UPDATE manquée soit une habitude et obtienne un verrou d'intervalle lorsque le niveau d'isolement de transaction est REPEATABLE-READ. En fonction du moment choisi, les situations suivantes se produiront et un blocage se produira.
Transaction1 | Transaction2 | commentaire |
---|---|---|
INSERT INTO ham_base (a ) VALUES (1); |
||
INSERT INTO ham_base (a ) VALUES (1); |
||
UPDATE ham_derived SET b = 2 WHERE ham_derived .base_ptr_id = 1; |
Verrouiller une acquisition | |
UPDATE ham_derived SET b = 2 WHERE ham_derived .base_ptr_id = 2; |
Acquisition de verrouillage B | |
INSERT INTO ham_derived (base_ptr_id , b ) VALUES (1, 2) |
En attente du verrou B | |
INSERT INTO ham_derived (base_ptr_id , b ) VALUES (2, 2) |
Puisqu'il attend le verrou A, le verrou mort est immédiatement détecté. |
Tout ce que vous avez à faire est d'indiquer clairement qu'il s'agit d'un INSERT. Vous pouvez l'éviter par les méthodes suivantes.
--Utilisez Model.objects.create ()
--Utilisez l'option force_insert
de Model.save ()
.
--Spécifiez select_on_save = True dans l'option méta du modèle.
> Determines if Django will use the pre-1.6 django.db.models.Model.save() algorithm. The old algorithm uses SELECT to determine if there is an existing row to be updated. The new algorithm tries an UPDATE directly.
Recommended Posts