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解析XML的几种常见方法的介绍
Apr 09 Python
Python编程中运用闭包时所需要注意的一些地方
May 02 Python
python实现类的静态变量用法实例
May 08 Python
在Python中操作字符串之replace()方法的使用
May 19 Python
深入解析Python中的变量和赋值运算符
Oct 12 Python
Python使用functools实现注解同步方法
Feb 06 Python
tensorflow 打印内存中的变量方法
Jul 30 Python
Python enumerate函数功能与用法示例
Mar 01 Python
解决pycharm同一目录下无法import其他文件
Feb 12 Python
Python matplotlib可视化实例解析
Jun 01 Python
Keras官方中文文档:性能评估Metrices详解
Jun 15 Python
python selenium 获取接口数据的实现
Dec 07 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 模拟登陆MSN并获得用户信息
2009/05/16 PHP
php新建文件自动编号的思路与实现
2011/06/27 PHP
iis下php mail函数的sendmail配置方法(官方推荐)
2012/04/25 PHP
使用php验证复选框有效性的示例
2013/11/13 PHP
php面向对象中static静态属性与方法的内存位置分析
2015/02/08 PHP
php实现的mongodb操作类实例
2015/04/03 PHP
CentOS 上搭建 PHP7 开发测试环境
2017/02/26 PHP
查看大图功能代码jquery版
2013/11/05 Javascript
dreamweaver 8实现Jquery自动提示
2014/12/04 Javascript
JavaScript中使用Math.floor()方法对数字取整
2015/06/15 Javascript
jquery实现页面常用的返回顶部效果
2016/03/04 Javascript
js仿QQ中对联系人向左滑动、滑出删除按钮的操作
2016/04/07 Javascript
jQuery Ajax 上传文件处理方式介绍(推荐)
2016/06/30 Javascript
微信小程序开发之map地图实现教程
2017/06/08 Javascript
js弹性势能动画之抛物线运动实例详解
2017/07/27 Javascript
微信小程序实现下载进度条的方法
2017/12/08 Javascript
element-ui table组件如何使用render属性的实现
2019/11/04 Javascript
Taro小程序自定义顶部导航栏功能的实现
2020/12/17 Javascript
跟老齐学Python之字典,你还记得吗?
2014/09/20 Python
编写Python的web框架中的Model的教程
2015/04/29 Python
Django中数据库的数据关系:一对一,一对多,多对多
2018/10/21 Python
解决nohup执行python程序log文件写入不及时的问题
2019/01/14 Python
如何用Python破解wifi密码过程详解
2019/07/12 Python
解决更改AUTH_USER_MODEL后出现的问题
2020/05/14 Python
浅析Python 简单工厂模式和工厂方法模式的优缺点
2020/07/13 Python
在vscode中启动conda虚拟环境的思路详解
2020/12/25 Python
泰坦健身器材:Titan Fitness
2018/02/13 全球购物
ReVive利维肤美国官网:RéVive Skincare
2018/04/18 全球购物
俄罗斯玩具、儿童用品、儿童服装和鞋子网上商店:MyToys.ru
2019/10/14 全球购物
what is the difference between ext2 and ext3
2015/08/25 面试题
导游个人求职信范文
2014/03/23 职场文书
感恩教师节演讲稿
2014/09/03 职场文书
大学新生军训自我鉴定范文
2014/09/13 职场文书
2014年调度员工作总结
2014/11/19 职场文书
python实现简单倒计时功能
2021/04/21 Python
MySQL分区以及建索引的方法总结
2022/04/13 MySQL