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进阶-函数默认参数(详解)
May 18 Python
在Python中调用Ping命令,批量IP的方法
Jan 26 Python
Python实现监控Nginx配置文件的不同并发送邮件报警功能示例
Feb 26 Python
Python二叉树的镜像转换实现方法示例
Mar 06 Python
python pytest进阶之fixture详解
Jun 27 Python
python config文件的读写操作示例
Sep 27 Python
Django restframework 框架认证、权限、限流用法示例
Dec 21 Python
Django 自定义权限管理系统详解(通过中间件认证)
Mar 11 Python
python实现梯度下降和逻辑回归
Mar 24 Python
Python异常处理机制结构实例解析
Jul 23 Python
关于python scrapy中添加cookie踩坑记录
Nov 17 Python
Python批量将csv文件转化成xml文件的实例
May 10 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连接Oracle数据库
2006/10/09 PHP
基于mysql的bbs设计(四)
2006/10/09 PHP
ThinkPHP行为扩展Behavior应用实例详解
2014/07/22 PHP
jquery 简单的进度条实现代码
2010/03/11 Javascript
基于JQuery的动态删除Table表格的行和列的代码
2011/05/12 Javascript
jQuery调用AJAX时Get和post公用的乱码解决方法实例说明
2013/06/04 Javascript
node.js实现爬虫教程
2020/08/25 Javascript
JS中对Cookie的操作详解
2016/08/05 Javascript
AngularJs  Creating Services详解及示例代码
2016/09/02 Javascript
BootStrap tooltip提示框使用小结
2016/10/26 Javascript
深入解析js轮播插件核心代码的实现过程
2017/04/14 Javascript
jQuery鼠标移动图片上实现放大效果
2017/06/25 jQuery
Node.js 基础教程之全局对象
2017/08/06 Javascript
使用原生js封装的ajax实例(兼容jsonp)
2017/10/12 Javascript
axios进阶实践之利用最优雅的方式写ajax请求
2017/12/20 Javascript
jQuery实现表单动态添加数据并提交的方法
2018/07/19 jQuery
解决VUEX的mapState/...mapState等取值问题
2020/07/24 Javascript
[51:29]完美世界DOTA2联赛循环赛 Matador vs Forest BO2第一场 11.05
2020/11/05 DOTA
跟老齐学Python之赋值,简单也不简单
2014/09/24 Python
Python中自定义函数的教程
2015/04/27 Python
Python的randrange()方法使用教程
2015/05/15 Python
python版学生管理系统
2018/01/10 Python
用python简单实现mysql数据同步到ElasticSearch的教程
2018/05/30 Python
Python实现报警信息实时发送至邮箱功能(实例代码)
2019/11/11 Python
python3 自动打印出最新版本执行的mysql2redis实例
2020/04/09 Python
详解Python中的路径问题
2020/09/02 Python
css3的transform中scale缩放详解
2014/12/08 HTML / CSS
RIP版本1跟版本2的区别
2013/12/30 面试题
环境科学专业个人求职信
2013/12/15 职场文书
小学生秋游活动方案
2014/02/23 职场文书
国贸专业求职信
2014/06/28 职场文书
医学生求职信
2014/07/01 职场文书
公司处罚决定书
2015/06/24 职场文书
清洁工工作总结
2015/08/11 职场文书
导游词之上海东方明珠塔
2019/09/25 职场文书
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
2021/04/05 MySQL