Le contenu de cet article concerne la résolution du problème de la perte de connexion à la base de données Django (explication avec exemples). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Problème
Lors de l'utilisation de MySQL dans Django, la connexion à la base de données peut parfois être perdue. Les erreurs incluent généralement les deux types suivants
1. `OperationalError: (2006, 'MySQL server has gone away')` 1. `OperationalError: (2013, 'Lost connection to MySQL server during query')`
Interroger MySQL. variable globale SHOW GLOBAL VARIABLES ; Vous pouvez voir wait_timeout, cette variable représente le temps d'inactivité de la connexion. Si le client utilise une connexion pour interroger la base de données plusieurs fois, il n'y aura aucun problème si la requête est continue. Si la requête s'arrête pendant plus de wait_timeout après plusieurs requêtes, la connexion à la base de données sera perdue et la connexion à la base de données sera perdue.
Reproduction
Utilisez Django pour reproduire le problème la prochaine fois :
1 Définissez le wait_timeout de mysql sur 10 secondes, puis entrez le django. Requête de simulation de shell (seule une partie du message d'erreur suivant est conservée)
In[1]:import time In[2]:from django.contrib.auth.models import User In[3]:list(User.objects.filter(id=1)) Out[3]:[<User: admin>] In[4]:time.sleep(15) # 模拟比较慢的代码(其中没有查询数据库的代码),或者空闲什么都不操作一段时间,此时间要比`wait_timeout`大一些 list(User.objects.filter(id=1)) Traceback (most recent call last): File "<ipython-input-4-3574ae8220ee>", line 1, in <module> list(User.objects.filter(id=1)) File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 1037, in _read_bytes CR.CR_SERVER_LOST, "Lost connection to MySQL server during query") django.db.utils.OperationalError: (2013, 'Lost connection to MySQL server during query')
Recherche
Ensuite, le problème ci-dessus montre essentiellement que l'erreur est causée par une durée trop longue temps d'inactivité.
Afin de réduire les connexions et fermetures inutiles de base de données, Django réutilise les connexions de base de données lorsqu'une requête est lancée, un pool de connexions est établi pour stocker la connexion. Après cela, une connexion est réutilisée pour chaque requête. On suppose que Django enregistre la connexion plus longtemps que wait_timeout. Si le temps de sauvegarde est plus court, la connexion peut être rétablie pour éviter cette erreur.
Oui, le document officiel a également expliqué ce problème, en définissant le paramètre CONN_MAX_AGE de la base de données, exemple :
DATABASES = { "default": { 'ENGINE': 'django.db.backends.mysql', 'NAME': '', 'USER': '', 'PASSWORD': '', 'HOST': '', 'CONN_MAX_AGE': 9 # 比wait_timeout小一些 } }
Lorsque nous l'avons testé, nous avons constaté que les choses n'étaient pas aussi simples que nous le pensions. Pourquoi l'erreur persiste-t-elle ? Derrière tout cela, est-ce la distorsion de la nature humaine ou la perte de la moralité ? Veuillez regarder le prochain épisode « Breakthrough ».
Breakthrough
a effectué une recherche de CONN_MAX_AGE
dans le code source de Django, a suivi les indices et a découvert comment Django ferme la connexion défaillante django.db.close_old_connections()
:
# Register an event to reset transaction state and close connections past # their lifetime. def close_old_connections(**kwargs): for conn in connections.all(): conn.close_if_unusable_or_obsolete() signals.request_started.connect(close_old_connections) signals.request_finished.connect(close_old_connections)
L'accent est mis sur les deux dernières lignes. Cette méthode est exécutée lorsque des événements spécifiques sont implémentés via un signal. Les deux événements spécifiques, comme leurs noms l'indiquent, sont le début de la requête et la fin de la requête. demande. L'erreur que nous avons signalée concernait une requête, cette méthode est donc généralement inefficace. Elle ferme et rétablit uniquement la connexion pour chaque requête.
Pour résoudre le problème
Ne fermez pas le shell Django qui reproduit le problème. Continuez à exécuter le code suivant :
In[5]:from django.db import close_old_connections In[6]:close_old_connections() In[7]:list(User.objects.filter(id=1)) Out[7]: [<User: admin>]
Appelez. django.db.close_old_connections à nouveau. Il n'y a aucune erreur dans la requête.
Ensuite, si nous voulons éviter cette erreur, nous devons appeler la méthode django.db.close_old_connections avant d'exécuter chaque requête de base de données.
1. Généralement, ce type de problème ne se produira pas, car la requête de base de données est effectuée en continu en une seule requête, et il n'est pas nécessaire d'appeler cette méthode pour chaque requête, ce qui est déraisonnable.
2. Parfois, la quantité de données dans une requête est importante, et la base de données sera interrogée, puis d'autres traitements (n'impliquant pas la base de données) seront effectués pendant un certain temps, comme l'interrogation préalable de certaines données. , puis traiter les données, générer Excel, enregistrer le fichier et générer l'URL. On sait que cela prend beaucoup de temps, il est donc préférable d'appeler d'abord django.db.close_old_connections pour éviter que la connexion ne soit perdue lorsque l'URL finale est enregistrée dans la base de données.
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!