Django数据库连接丢失问题的解决方法


Posted in Python onDecember 29, 2018

问题

在Django中使用mysql偶尔会出现数据库连接丢失的情况,错误通常有如下两种

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

查询mysql全局变量SHOW GLOBAL VARIABLES;可以看到wait_timeout,此变量表示连接空闲时间。如果客户端使用一个连接查询多次数据库,如果连续查询则没有问题,如果查询几次后停顿超过wait_timeout后再次查询就会出现数据库连接丢失。

复现

下面用Django复现下次问题:

将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方法。

一般情况不会出现此类问题,因为一个请求中不间断进行数据库查询,无需每个请求调用此方法,杞人忧天。

有时候一个请求中数据量较大,会查询数据库后进行一段时间其他(不涉及数据库)处理,比如先查询一些数据,然后将数据处理、生成excel、保存文件并生成url。已知此过长需要非常长时间,那么最终url保存数据库就最好先调用django.db.close_old_connections防止连接丢失

题外话 实际上②所述情况最好从根本上解决处理慢的问题,也可以换作异步处理,从根本上解决问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python基于回溯法子集树模板解决找零问题示例
Sep 11 Python
浅谈python中copy和deepcopy中的区别
Oct 23 Python
python之virtualenv的简单使用方法(必看篇)
Nov 25 Python
对python中不同模块(函数、类、变量)的调用详解
Jul 16 Python
详解用python计算阶乘的几种方法
Aug 14 Python
python利用JMeter测试Tornado的多线程
Jan 12 Python
Python paramiko 模块浅谈与SSH主要功能模拟解析
Feb 29 Python
520使用Python实现“我爱你”表白
May 20 Python
keras实现调用自己训练的模型,并去掉全连接层
Jun 09 Python
浅谈keras中的batch_dot,dot方法和TensorFlow的matmul
Jun 18 Python
Python容器类型公共方法总结
Aug 19 Python
python制作抽奖程序代码详解
Jan 15 Python
Python Cookie 读取和保存方法
Dec 28 #Python
Python编程flask使用页面模版的方法
Dec 28 #Python
Python编程中flask的简介与简单使用
Dec 28 #Python
Python3 Post登录并且保存cookie登录其他页面的方法
Dec 28 #Python
Python3 使用cookiejar管理cookie的方法
Dec 28 #Python
Python编程在flask中模拟进行Restful的CRUD操作
Dec 28 #Python
python获取服务器响应cookie的实例
Dec 28 #Python
You might like
PHP简洁函数小结
2011/08/12 PHP
PHP利用str_replace防注入的方法
2013/11/10 PHP
php遍历树的常用方法汇总
2015/06/18 PHP
完美解决Thinkphp3.2中插入相同数据的问题
2017/08/01 PHP
事件模型在各浏览器中存在差异
2010/10/20 Javascript
javascript实现图片轮播效果
2016/01/20 Javascript
React.js入门学习第一篇
2016/03/30 Javascript
Javascript中的async awai的用法
2017/05/17 Javascript
jQuery选择器之基本过滤选择器用法实例分析
2019/02/19 jQuery
vue axios post发送复杂对象问题
2019/06/04 Javascript
在vue和element-ui的table中实现分页复选功能
2019/12/04 Javascript
详解JavaScript自定义函数
2020/07/29 Javascript
[55:03]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第二场 11.20
2020/11/20 DOTA
一篇不错的Python入门教程
2007/02/08 Python
Python使用filetype精确判断文件类型
2017/07/02 Python
python socket网络编程之粘包问题详解
2018/04/28 Python
python3实现163邮箱SMTP发送邮件
2018/05/22 Python
详解利用Python scipy.signal.filtfilt() 实现信号滤波
2019/06/05 Python
Python3离线安装Requests模块问题
2019/10/13 Python
django框架forms组件用法实例详解
2019/12/10 Python
如何基于python操作excel并获取内容
2019/12/24 Python
用python拟合等角螺线的实现示例
2019/12/27 Python
Python捕获异常堆栈信息的几种方法(小结)
2020/05/18 Python
python实现自动打卡的示例代码
2020/10/10 Python
美国知名男士服饰品牌:Brooks Brothers(布克兄弟)
2016/08/25 全球购物
Under Armour澳大利亚官网:美国知名的高端功能性运动品牌
2018/02/22 全球购物
意大利辅助药品、药物和补品在线销售:FarmaEurope
2020/04/29 全球购物
一百多行代码实现react拖拽hooks
2021/03/23 Javascript
军校本科大学生自我评价
2014/01/14 职场文书
大学毕业感言200字
2014/03/09 职场文书
中学清明节活动总结
2014/07/04 职场文书
初中信息技术教学计划
2015/01/22 职场文书
阿里云服务器部署mongodb的详细过程
2021/09/04 MongoDB
Python爬虫入门案例之回车桌面壁纸网美女图片采集
2021/10/16 Python
Vue.js中v-bind指令的用法介绍
2022/03/13 Vue.js
Go调用Rust方法及外部函数接口前置
2022/06/14 Golang