Heim  >  Artikel  >  Backend-Entwicklung  >  Lösung des Problems des Verbindungsverlusts der Django-Datenbank (Erklärung mit Beispielen)

Lösung des Problems des Verbindungsverlusts der Django-Datenbank (Erklärung mit Beispielen)

不言
不言nach vorne
2018-12-29 10:35:433794Durchsuche

Der Inhalt dieses Artikels befasst sich mit der Lösung des Problems des Verbindungsverlusts der Django-Datenbank (Erklärung mit Beispielen). Ich hoffe, dass er für Sie hilfreich ist.

Problem

Bei der Verwendung von MySQL in Django kann die Datenbankverbindung gelegentlich verloren gehen. Die Fehler umfassen normalerweise die folgenden zwei Typen

1. `OperationalError: (2006, 'MySQL server has gone away')`  
1. `OperationalError: (2013, 'Lost connection to MySQL server during query')`

Query mysql globale Variablen SHOW GLOBALE VARIABLEN ;Sie können wait_timeout sehen. Diese Variable repräsentiert die Leerlaufzeit der Verbindung. Wenn der Client eine Verbindung mehrmals zum Abfragen der Datenbank verwendet, gibt es kein Problem, wenn die Abfrage nach mehreren Abfragen länger als „wait_timeout“ pausiert, wird die Datenbankverbindung unterbrochen und die Datenbankverbindung wird unterbrochen.

Reproduktion

Verwenden Sie Django, um das Problem das nächste Mal zu reproduzieren:

1. Stellen Sie den wait_timeout von MySQL auf 10 Sekunden ein und geben Sie dann Django ein Shell-Simulationsabfrage (nur ein Teil der folgenden Fehlermeldung bleibt erhalten)

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')

Seek

Dann zeigt das obige Problem im Grunde, dass der Fehler durch eine zu lange Leerlaufzeit verursacht wird .
Um unnötige Datenbankverbindungen und Schließungen zu reduzieren, verwendet Django Datenbankverbindungen wieder. Wenn eine Anfrage gestartet wird, wird ein Verbindungspool zum Speichern der Verbindung eingerichtet. Danach wird für jede Anfrage eine Verbindung wiederverwendet. Ich vermute, dass Django die Verbindung länger speichert als „wait_timeout“. Wenn die Speicherzeit kürzer ist, kann die Verbindung wiederhergestellt werden, um diesen Fehler zu vermeiden.
Ja, das offizielle Dokument hat dieses Problem auch erklärt. Legen Sie den Datenbankparameter CONN_MAX_AGE fest, Beispiel:

DATABASES = {
    "default": {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': '',
            'USER': '',
            'PASSWORD': '',
            'HOST': '',
            'CONN_MAX_AGE': 9  # 比wait_timeout小一些
    }
}

Als wir es testeten, stellten wir fest, dass die Dinge nicht so einfach waren, wie wir dachten. Warum tritt der Fehler immer noch auf? Steckt dahinter die Verzerrung der menschlichen Natur oder der Verlust der Moral? Sehen Sie sich bitte die nächste Folge „Breakthrough“ an.

Breakthrough

führte eine Suche nach CONN_MAX_AGE im Django-Quellcode durch, folgte den Hinweisen und fand heraus, wie Django die fehlgeschlagene Verbindung schließtdjango.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)

Der Fokus liegt auf den letzten beiden Zeilen. Diese Methode wird ausgeführt, wenn bestimmte Ereignisse durch Signal implementiert werden. Die beiden spezifischen Ereignisse sind, wie der Name schon sagt, der Beginn der Anfrage und das Ende der Anfrage. Der von uns gemeldete Fehler trat in einer Anfrage auf, daher ist diese Methode normalerweise wirkungslos. Sie schließt die Verbindung nur für jede Anfrage und stellt sie wieder her.

Lösung

Schließen Sie nicht die Django-Shell, die das Problem reproduziert. Führen Sie weiterhin den folgenden Code aus:

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>]

Rufen Sie django.db.close_old_connections auf und erneut abfragen. Keine Fehler mehr.
Wenn wir diesen Fehler vermeiden wollen, müssen wir die Methode django.db.close_old_connections aufrufen, bevor wir jede Datenbankabfrage ausführen.

1. Im Allgemeinen tritt dieses Problem nicht auf, da die Datenbankabfrage kontinuierlich in einer Anfrage ausgeführt wird und diese Methode nicht für jede Anfrage aufgerufen werden muss, was unzumutbar ist.

2. Manchmal ist die Datenmenge in einer Anfrage groß und die Datenbank wird abgefragt und dann werden für einen bestimmten Zeitraum andere Verarbeitungen (die nicht die Datenbank betreffen) durchgeführt, z. B. zuerst einige Daten abfragen , dann die Daten verarbeiten, Excel generieren, die Datei speichern und eine URL generieren. Es ist bekannt, dass dies sehr lange dauert. Rufen Sie daher am besten zuerst django.db.close_old_connections auf, um zu verhindern, dass die Verbindung verloren geht, wenn die endgültige URL in der Datenbank gespeichert wird.

Das obige ist der detaillierte Inhalt vonLösung des Problems des Verbindungsverlusts der Django-Datenbank (Erklärung mit Beispielen). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen