首頁 > 後端開發 > Python教學 > Django資料庫連線遺失的問題解決(範例講解)

Django資料庫連線遺失的問題解決(範例講解)

不言
發布: 2018-12-29 10:35:43
轉載
3843 人瀏覽過

這篇文章帶給大家的內容是關於Django資料庫連線遺失的問題解決(範例講解),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

問題

在Django中使用mysql偶爾會出現資料庫連線遺失的情況,錯誤通常有以下兩種

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

查詢mysql全域變數SHOW GLOBAL VARIABLES ;可以看到wait_timeout,此變數表示連線空閒時間。如果用戶端使用連線查詢多次資料庫,如果連續查詢則沒有問題,如果查詢幾次後停頓超過wait_timeout後再次查詢就會出現資料庫連線遺失。

複現

下面用Django複現下次問題:

1、將mysql的wait_timeout設定為10秒,然後進入django shell模擬查詢(以下錯誤訊息只保留了部分)

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')
登入後複製

尋求

#那麼多問題就基本說明了是空閒時間過長導致的錯誤。 
django為了減少不必要的資料庫連接、關閉,復用了資料庫連接,當開始一個請求後建立一個連接池存放連接,之後此次請求都複用一個連接。那猜測就是django保存連線的比wait_timeout長了,如果保存時間短一些就可以重新建立連線避免此錯誤了。
沒錯,官方文件也已經說明了此問題,設定資料庫 CONN_MAX_AGE參數,範例:

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

當我們測試後卻發現,事情並非想想中那麼簡單。為何錯誤依舊出現?這一切的背後, 是人性的扭曲還是道德的淪喪?敬請收看下節《突破》。

突破

對django源碼中CONN_MAX_AGE進行了一番搜索,順藤摸瓜發現了django關閉失效連接的方法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)
登入後複製

重點在最後兩行,透過signal實作特定事件時執行此方法,兩個特定事件顧名思義是請求開始和請求結束。而我們報錯的是在一次請求中,所以此法通常無效,只是實作每個請求關閉並重新建立連線。

解決

復現問題的django shell不要關閉,繼續執行如下程式碼:

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>]
登入後複製

呼叫django.db.close_old_connections後再查詢就沒有錯誤了。
那麼我們要避免此錯誤就要執行每個資料庫查詢前呼叫django.db.close_old_connections方法。

1、一般情況不會出現此類問題,因為一個請求中不間斷進行資料庫查詢,無需每個請求調用此方法,杞人憂天。

2、有時候一個請求中資料量較大,會查詢資料庫後進行一段時間其他(不涉及資料庫)處理,例如先查詢一些數據,然後將資料處理、產生excel、儲存檔案並生成url。已知此過長需要非常長時間,那麼最終url保存資料庫就最好先呼叫django.db.close_old_connections防止連線遺失。

#

以上是Django資料庫連線遺失的問題解決(範例講解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:segmentfault.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板