Django, после обновления: сервер MySQL ушел
Недавно я обновился с Django 1.4 до Django 1.7, и поскольку я продолжаю получать следующее сообщение об ошибке для некоторых скриптов, иногда:
OperationalError: (2006, 'MySQL server has gone away')
Сценарии - это очень длинные или постоянно выполняемые задачи, которые могут включать фазы не связывания с db в течение нескольких минут, поэтому время соединения заканчивается. Однако, прежде чем я обновился, это не было проблемой, поскольку Django, казалось, автоматически восстановил соединение. Теперь это не означает, что задачи часто останавливаются и терпят неудачу посередине.
Кто-нибудь знает, что изменилось и как я могу это исправить?
Возможно, это связано с этим билетом/исправлением: https://code.djangoproject.com/ticket/21463
Спасибо большое!
Ответы
Ответ 1
Причиной такого поведения является постоянное подключение к базе данных, которое было представлено в Django 1.6.
Чтобы предотвратить ошибку времени ожидания соединения, вы должны установить для CONN_MAX_AGE
в settings.py
значение, которое меньше, чем wait_timeout
в конфигурации MySQL (my.cnf
). В этом случае Django обнаруживает, что соединение необходимо открыть раньше, чем MySQL его выбрасывает. Значение по умолчанию для MySQL 5.7 составляет 28800 секунд.
settings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 3600,
<other params here>
}
}
Документация: https://docs.djangoproject.com/en/1.7/ref/settings/#conn-max-age
my.cnf
:
wait_timeout = 28800
Документация: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout
Ответ 2
У меня есть работающий фоновый процесс rqworker
, который выполняет отдельные задания для обновления некоторых данных после некоторых действий пользователя.
Я всегда получаю OperationalError: (2006, 'MySQL server has gone away')
если в течение более чем wait_timeout
секунд не было действий пользователя.
Даже если я установил CONN_MAX_AGE меньше, чем MySQL wait_timeout
.
Как я понимаю, изменение CONN_MAX_AGE может помочь, если Django автоматически проверит и закроет свои подключения этим таймаутом. Но Django 1.7.x проверяет его до и после каждого запроса (см. django/db/ init.py # L101-L112).
Как описано в Django ticket 15119, мы видим, что Django делает ping для проверки того, было ли соединение живым до выполнения каждого запроса. Это поведение было зафиксировано в commit 282b2f4.
Разработчики Django дали короткий ответ на все вопросы, подобные этому в https://code.djangoproject.com/ticket/21597#comment:29
Поэтому мой rqworker
процесс должен проверять само соединение для каждого нового задания. (Примечание: если мы закроем соединение, то Django создаст новый).
Я собираюсь использовать подход Django для каждого запроса и называть django.db.close_old_connections()
до и после каждой работы. И да, CONN_MAX_AGE
должно быть меньше MySQL wait_timeout
, потому что Django не проверяет, есть ли MySQL server has gone away
в django.db.close_old_connections()
.
Ответ 3
На Джанго 1.9:
У меня была запущенная Django Shell на экране Unix, оставленная без присмотра более 48 часов.
Когда я вернулся к нему и запустил <some_model>.objects.filter
он бросил
OperationalError: (2006, 'MySQL server has gone away')
Быстрый import django.db; django.db.close_old_connections()
помог мне.
Я не смог найти документацию для close_old_connections()
в Django Docs для 1.9, однако здесь есть прямая ссылка на его реализацию в Django Codebase на Github.
Ответ 4
В django 1.6, когда wait_timeout прошло (из mysql), тогда доступ к БД вызвал ошибку (2006, "сервер MySQL ушел" ).
Это было не так в django 1.5.1
Я заметил эту ошибку при использовании рабочих, которые запускают код django (используя механизм).
Воспроизведение:
Установите тайм-аут на низкое значение, отредактировав /etc/mysql/my.cnf
добавьте следующее в [mysqld]
wait_timeout = 10
interactive_timeout = 10
Тогда
% python manage.py shell
>>> # access DB
>>> import django.contrib.auth.models
>>> print list(django.contrib.auth.models.User.objects.all())
>>> import time
>>> time.sleep(15)
>>> print list(django.contrib.auth.models.User.objects.all())
Теперь вы получите сообщение об ошибке.
Простым решением, которое я нашел в Интернете, является вызов django.db.close_connection() перед доступом
>>> import django.db
>>> django.db.close_connection()
>>> print list(django.contrib.auth.models.User.objects.all())
работает нормально.
Ответ 5
Мы тоже это заметили. Ответ выше установки CONN_MAX_AGE на что-то меньшее, чем MySQL/MariaDB wait_timeout работает - для Интернета.
Однако для долгосрочных задач это, похоже, не работает. Вместо этого мы завернули его и закрывали соединение каждый раз, когда выполняется одна из наших долгосрочных задач.
Мы объединяем это с нашим собственным пулом. Возьми или оставь это - по умолчанию Django имеет нулевой контроль - не то, что нам понравилось в производстве. мы устанавливаем максимальный пул для уничтожения сервера до того, как он убьет БД с большим количеством соединений. Используйте его как декоратор для своих задач:
@close_db_connection()
def task_do_something():
print 'Hello'
'''
Created on Dec 23, 2017
@author: Kevin
'''
from functools import wraps
def close_db_connection(ExceptionToCheck=Exception, raise_exception=False, notify=False):
"""Close the database connection when we're finished, django will have to get a new one..."""
def deco_wrap(f):
@wraps(f)
def f_wrap(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
raise e
finally:
from django.db import connection;
connection.close();
return f_wrap
return deco_wrap
Ответ 6
Возможно, тайм-аут является проблемой для некоторых, но я столкнулся с этой проблемой при попытке написать очень большое поле BLOB. Я разрешил его, увеличив максимально допустимый размер пакета в файле конфигурации mysql...
max_allowed_packet = 4M
Не забудьте перезапустить mysql после изменения на /etc/my.cnf. Эта страница помогла...
http://dev.mysql.com/doc/refman/5.5/en/packet-too-large.html