为什么我在 Django 中收到“MySQL 服务器已经消失”异常?

Joh*_*ohn 9 python mysql django python-3.x

我正在使用 Django 2.2.6。

运行我的 django 项目的同一个系统也有一个后台服务在运行,它在 unix 套接字上监听请求。在 Django Admin 中,如果用户点击按钮,Django 会在 unix socket 上发送请求,后台服务会做一些事情。

我的后台服务可以完全访问 Django 的 ORM。它从我的项目的models.py 中导入模型,并且可以毫无问题地查询数据库。

问题是,如果我离开我的 django,我的后台服务在一夜之间运行,登录到 Django Admin,然后点击按钮,我的后台服务会抛出一个异常:

django.db.utils.OperationalError: (2006, 'MySQL server has gone away')
Run Code Online (Sandbox Code Playgroud)

看来这是因为 MySQL 数据库有一个超时期限,称为wait_timeout. 如果到数据库的连接长时间不活动,MySQL 将断开它。不幸的是,Django 没有注意到,并尝试使用它,抛出错误。

幸运的是,Django 为 settings.py 中定义的每个数据库都有自己的内置 CONN_MAX_AGE 变量。如果数据库连接比 CONN_MAX_AGE 旧,它会在请求之前关闭它并启动一个新的连接。

查看我的 MySQL 数据库:

> show session variables where variable_name = "wait_timeout";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
Run Code Online (Sandbox Code Playgroud)

查看我的 Django 的 CONN_MAX_AGE 变量:

# ./manage.py shell
>>> from django.conf import settings
>>> settings.DATABASES['default']['CONN_MAX_AGE']
0
Run Code Online (Sandbox Code Playgroud)

注意:“默认”数据库是我在 settings.py 中定义的唯一数据库

另请注意,我的 MySQL wait_timeout 和 Django 的 CONN_MAX_AGE 都是默认值 - 我没有更改它们。

根据此处的 Django 文档,CONN_MAX_AGE 值为 0 表示:

在每个请求结束时关闭数据库连接

如果 django 打算在每次请求后关闭数据库连接,为什么我会遇到这个错误?为什么它不在我运行查询后关闭旧连接,并在几小时后执行新查询时启动新连接?

编辑:

现在,我的解决方案是让我的后台服务进行心跳。每小时一次似乎工作正常。心跳只是一个简单的、低资源消耗的 MySQL 命令,如 MyDjangoModel.objects.exists()。只要它执行 MySQL 查询以刷新 MySQL 超时,它就可以工作。这样做确实给我的后台服务增加了一些复杂性,因为在一种情况下,我的其他单线程后台服务需要一个后台线程,其唯一的工作是进行心跳。

如果对此有更简单的解决方案,或者至少解释为什么会发生这种情况,我想听听。

小智 8

我遇到了和你完全相同的问题。我使用看门狗库实现了一个监视脚本,并且在“wait_timeout”结束时,将引发 MySQL 错误。

经过几次尝试“django.db.close_old_connections()”函数,它仍然不起作用,但我试图在每个定义的时间间隔关闭旧连接,但这不起作用。我将 close 命令更改为仅在调用自定义管理命令(该命令将与 db 交互并用于因 MySQL 错误而崩溃)之前运行,并且它开始工作。

显然从这个页面来看,发生这种情况的原因是因为“close_old_connection”函数仅链接到HTTP请求信号,因此它不会在特定的自定义脚本中被触发。Django 的文档没有说明这一点,老实说我也以与您理解的方式相同的方式理解事物。

因此,您可以尝试做的是在与数据库交互之前添加调用以关闭旧连接:

from django.db import close_old_connections
close_old_connections()
do_something_with_db()
Run Code Online (Sandbox Code Playgroud)