nKn*_*nKn 6 python mysql django mysql-python
我正在构建一个 Django(版本 3.0.5)应用程序,它使用mysqlclient(版本 2.0.3)作为数据库后端。此外,我编写了一个 Django 命令,该命令运行使用python-telegram-bot API 编写的机器人,因此该机器人的任务是无限期运行,因为它必须随时响应命令。
问题是大约24小时。运行机器人后(不一定一直空闲),django.db.utils.OperationalError: (2006, 'MySQL server has gone away')运行任何命令后都会出现异常。
我绝对确定 MySQL 服务器一直在运行,并且在我收到此异常时仍在运行。MySQL 服务器版本是5.7.35.
我的假设是一些 MySQL 线程老化并关闭,因此在重用它们后它们不会被更新。
有谁遇到过这种情况并且知道如何解决吗?
Traceback (most recent call last):
File "/opt/django/gip/venv/lib/python3.6/site-packages/telegram/ext/dispatcher.py", line 555, in process_update
handler.handle_update(update, self, check, context)
File "/opt/django/gip/venv/lib/python3.6/site-packages/telegram/ext/handler.py", line 198, in handle_update
return self.callback(update, context)
File "/opt/django/gip/gip/hospital/gipcrbot.py", line 114, in ayuda
perfil = get_permiso_efectivo(update.message.from_user.id)
File "/opt/django/gip/gip/hospital/telegram/funciones.py", line 33, in get_permiso_efectivo
u = Telegram.objects.get(idtelegram=userid)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 411, in get
num = len(clone)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 258, in __len__
self._fetch_all()
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 1261, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 57, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1151, in execute_sql
cursor.execute(sql, params)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
return super().execute(sql, params)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
return self.cursor.execute(sql, params)
File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 74, in execute
return self.cursor.execute(query, args)
File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 206, in execute
res = self._query(query)
File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 319, in _query
db.query(q)
File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/connections.py", line 259, in query
_mysql.connection.query(self, query)
django.db.utils.OperationalError: (2006, 'MySQL server has gone away')
Run Code Online (Sandbox Code Playgroud)
我已经尝试更改 Django settings.py 文件,因此我为 设定了一个显式值CONN_MAX_AGE,并且还为 MySQL 客户端wait_timeout参数设置了一个值,该CONN_MAX_AGE值低于wait_timeout。
设置.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': '/opt/django/gip/gip/gip/my.cnf',
},
'CONN_MAX_AGE': 3600,
}
}
Run Code Online (Sandbox Code Playgroud)
我的.cnf:
[client]
...
wait_timeout = 28800
Run Code Online (Sandbox Code Playgroud)
不幸的是,行为完全相同:我在大约 24 小时内收到异常。运行机器人后。
设置CONN_MAX_AGE为None也不会产生任何影响。
我mysql-server-has-gone-away按照 @r-marolahy 的建议安装了 python 包,但它也不会产生任何影响。运行近 24 小时后,“消失”消息再次显示。
我还尝试了关闭旧连接的方法:
from django.db import close_old_connections
try:
#do your long running operation here
except django.db.utils.OperationalError:
close_old_connections()
#do your long running operation here
Run Code Online (Sandbox Code Playgroud)
仍然得到相同的结果。
当 MySQL 由于服务器超时而关闭连接时,django 发生了此错误。要启用持久连接,请设置CONN_MAX_AGE为秒的正整数或将其设置为None无限制的持久连接 ( source )。
Update1: 如果上面建议的解决方案不起作用,您可能想尝试mysql-server-has-gone-away包。我还没有尝试过,但在这种情况下可能会有所帮助。
Update2:另一种尝试是尝试使用try/except语句来捕获此问题OperationalError并重置与 的连接close_old_connections。
from django.db import close_old_connections
try:
#do your long running operation here
except django.db.utils.OperationalError:
close_old_connections()
#do your long running operation here
Run Code Online (Sandbox Code Playgroud)
update3:如此处所述
Django ORM 是一段同步代码,因此如果您想从异步代码访问它,您需要进行特殊处理以确保其连接正确关闭。
然而,Django ORM 使用asgiref.sync.sync_to_async适配器,该适配器仅在 MySQL 关闭连接之前起作用。在这种情况下,使用channels.db.database_sync_to_async(SyncToAsync退出时清除旧数据库连接的版本)可能会解决此问题。
您可以像下面这样使用它(来源):
from channels.db import database_sync_to_async
async def connect(self):
self.username = await database_sync_to_async(self.get_name)()
def get_name(self):
return User.objects.all()[0].name
Run Code Online (Sandbox Code Playgroud)
或将其用作装饰器:
@database_sync_to_async
def get_name(self):
return User.objects.all()[0].name
Run Code Online (Sandbox Code Playgroud)
确保首先遵循此处的安装说明。
我最终在机器人中每 X 小时(在本例中为 6 小时)安排一次数据库查询。有一个名为JobQueuepython-telegram-bot的类,它有一个名为 的方法。这将每n秒运行一个任务。所以我宣布:run_repeating
def check_db(context):
# Do the code for running "SELECT 1" in the DB
return
updater.job_queue.run_repeating(check_db, interval=21600, first=21600)
Run Code Online (Sandbox Code Playgroud)
经过这次更改后,我没有再遇到同样的问题。
close_if_unusable_or_obsolete()另外,在我的例子中,时不时调用大多数未记录的Django 方法也有效。
from django.db import connection
connection.close_if_unusable_or_obsolete()
Run Code Online (Sandbox Code Playgroud)