【发布时间】:2020-05-03 12:49:10
【问题描述】:
我正在使用 Django 2.2.6。
运行我的 django 项目的同一系统也运行着一个后台服务,它在 unix 套接字上侦听请求。在 Django Admin 中,如果用户点击一个按钮,Django 会在 unix 套接字上发送一个请求,而后台服务会做一些事情。
我的后台服务可以完全访问 Django 的 ORM。它从我项目的 models.py 中导入模型,并且可以毫无问题地查询数据库。
问题是,如果我离开我的 django,而我的后台服务在一夜之间运行,登录到 Django Admin,然后点击按钮,我的后台服务会抛出异常:
django.db.utils.OperationalError: (2006, 'MySQL server has gone away')
这似乎是因为 MySQL 数据库有一个超时期限,称为wait_timeout。如果与数据库的连接在很长一段时间内未处于活动状态,MySQL 将断开它。不幸的是,Django 没有注意到,并尝试使用它,抛出错误。
幸运的是,对于 settings.py 中定义的每个数据库,Django 都有自己的内置 CONN_MAX_AGE 变量。如果数据库连接早于 CONN_MAX_AGE,它会在请求之前将其关闭并启动一个新连接。
查看我的 MySQL 数据库:
> show session variables where variable_name = "wait_timeout";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
查看我的 Django 的 CONN_MAX_AGE 变量:
# ./manage.py shell
>>> from django.conf import settings
>>> settings.DATABASES['default']['CONN_MAX_AGE']
0
注意:“默认”数据库是我在 settings.py 中定义的唯一数据库
还要注意,我的 MySQL wait_timeout 和 Django 的 CONN_MAX_AGE 都是默认值 - 我没有更改它们。
根据 Django 文档here,CONN_MAX_AGE 值为 0 意味着:
在每次请求结束时关闭数据库连接
如果 django 打算在每次请求后关闭数据库连接,为什么我会遇到这个错误?为什么它没有在我运行完查询后关闭旧连接,并在几小时后执行新查询时启动新连接?
编辑:
现在,我的解决方案是让我的后台服务进行心跳。每小时一次似乎工作正常。心跳只是一个简单的、低资源消耗的 MySQL 命令,如 MyDjangoModel.objects.exists()。只要它执行 MySQL 查询以刷新 MySQL 超时,它就可以工作。这样做确实给我的后台服务增加了一些复杂性,因为在一种情况下,我原本是单线程的后台服务需要一个后台线程,其唯一的工作就是执行心跳。
如果有更简单的解决方案,或者至少解释为什么会发生这种情况,我想听听。
【问题讨论】:
-
您可以在 MySQL 的配置中应用 log_warnings=2 以在连接因 wait_timeout 到期而终止时记录在错误日志中。我和@John 在一起,如果 django 自动关闭连接,他们应该默默地重新连接以避免麻烦。
-
您找到解决方案了吗?请注意,根据您运行 Django 的方式,套接字描述符可能会在进程等之间共享,从而导致 mysql 连接出现各种意外。一般来说,这是分叉服务器或脚本中的一个微妙问题。我只是认为您使用附加后台服务的特殊设置可能会导致与此相关的一些问题。
-
我的解决方案是使用后台心跳线程。我正在寻找的是为什么会发生这种情况的解释,以及避免使用心跳线程的方法,到目前为止,我还没有找到任何一个。
标签: python mysql django python-3.x