Подтвердить что ты не робот

Python и Django OperationalError (2006, "сервер MySQL ушел" )

Оригинал: Я недавно начал получать MySQL OperationalErrors из некоторого моего старого кода и, похоже, не смог отследить проблему. Поскольку он работал раньше, я подумал, что это может быть обновление программного обеспечения, которое что-то сломало. Я использую python 2.7 с django runfcgi с nginx. Вот мой оригинальный код:

views.py

DBNAME = "test"
DBIP = "localhost"
DBUSER = "django"
DBPASS = "password"
db = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
cursor = db.cursor()

def list(request):
    statement = "SELECT item from table where selected = 1"
    cursor.execute(statement)
    results = cursor.fetchall()

Я пробовал следующее, но он все еще не работает:

views.py

class DB:
    conn = None
    DBNAME = "test"
    DBIP = "localhost"
    DBUSER = "django"
    DBPASS = "password"
def connect(self):
    self.conn = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
def cursor(self):
    try:
        return self.conn.cursor()
    except (AttributeError, MySQLdb.OperationalError):
        self.connect()
        return self.conn.cursor()

db = DB()
cursor = db.cursor()

def list(request):
    cursor = db.cursor()
    statement = "SELECT item from table where selected = 1"
    cursor.execute(statement)
    results = cursor.fetchall()

В настоящее время единственным моим решением является MySQLdb.connect() в каждой функции, использующей mysql. Также я заметил, что при использовании django manage.py runserver у меня не было бы этой проблемы, в то время как nginx выбрасывал эти ошибки. Я сомневаюсь, что я устал от подключения, потому что list() вызывается в течение нескольких секунд после запуска сервера. Были ли какие-либо обновления программного обеспечения, которое я использую, чтобы это могло нарушить/есть ли какое-либо исправление для этого?

Изменить: я понял, что недавно написал часть среднего уровня для демонстрации функции, и это стало причиной проблемы. Однако я не могу понять, почему. Вот код для среднего уровня

def process_request_handler(sender, **kwargs):
    t = threading.Thread(target=dispatch.execute,
        args=[kwargs['nodes'],kwargs['callback']],
        kwargs={})
    t.setDaemon(True)
    t.start()
    return
process_request.connect(process_request_handler)
4b9b3361

Ответ 1

Согласно документации MySQL, ваше сообщение об ошибке возникает, когда клиент не может отправить вопрос на сервер, скорее всего потому, что сервер сам закрыл соединение. В наиболее распространенном случае сервер закроет незанятое соединение через (по умолчанию) 8 часов. Это настраивается на стороне сервера.

Документация MySQL содержит ряд других возможных причин, которые стоит изучить, чтобы понять, соответствуют ли они вашей ситуации.

Альтернативой вызову connect() в каждой функции (что может привести к ненужному созданию новых соединений) было бы исследование с использованием метода ping() объекта соединения; это проверяет соединение с возможностью попытки автоматического переподключения. Я изо всех сил пытался найти приличную документацию для метода ping() онлайн, но ответ на этот вопрос мог бы помочь.

Обратите внимание, что автоматическое переподключение может быть опасным при обработке транзакций, так как кажется, что переподключение вызывает неявный откат (и, по-видимому, является основной причиной, по которой автоматическое переподключение не является функцией реализации MySQLdb).

Ответ 2

Иногда, если вы видите "OperationalError: (2006," сервер MySQL ушел "), это связано с тем, что вы выдаете слишком большой запрос. Это может произойти, например, если вы храните свои сессии в MySQL, и вы пытаетесь поставить что-то действительно большое в сеансе. Чтобы устранить проблему, вам нужно увеличить значение параметра max_allowed_packet в MySQL.

Значение по умолчанию - 1048576.

Итак, посмотрите текущее значение по умолчанию, запустите следующий SQL:

select @@max_allowed_packet;

Чтобы временно установить новое значение, запустите следующий SQL:

set global max_allowed_packet=10485760;

Чтобы устранить проблему более долго, создайте файл /etc/my.cnf, по крайней мере, следующим образом:

[mysqld]
max_allowed_packet = 16M

После редактирования /etc/my.cnf вам нужно перезапустить MySQL или перезагрузить компьютер, если вы не знаете, как это сделать.

Ответ 3

Проверьте, разрешено ли вам создавать объект соединения mysql в одном потоке, а затем использовать его в другом.

Если это запрещено, используйте threading.Local для соединений с потоком:

class Db(threading.local):
    """ thread-local db object """
    con = None

    def __init__(self, ...options...):
        super(Db, self).__init__()
        self.con = MySQLdb.connect(...options...)

db1 = Db(...)


def test():
    """safe to run from any thread"""
    cursor = db.con.cursor()
    cursor.execute(...)

Ответ 4

Я тоже борюсь с этой проблемой. Мне не нравится идея увеличения тайм-аута на mysqlserver. Autoreconnect с CONNECTION_MAX_AGE не работает, как было упомянуто. К сожалению, я закончил с упаковкой каждого метода, который запрашивает базу данных, такую ​​как

def do_db( callback, *arg, **args):
    try:
        return callback(*arg, **args)
    except (OperationalError, InterfaceError) as e:  # Connection has gone away, fiter it with message or error code if you could catch another errors
        connection.close()
        return callback(*arg, **args)

do_db(User.objects.get, id=123)  # instead of User.objects.get(id=123)

Как вы можете видеть, я предпочитаю перехватывать исключение, а не пинговать базу данных каждый раз, прежде чем запрашивать ее. Потому что ловить исключение - редкий случай. Я ожидал бы, что django автоматически подключится, но они, похоже, отказались от этой проблемы.

Ответ 5

Сколько лет этот код? У Django были базы данных, определенные в настройках, по крайней мере .96. Единственное, что я могу придумать, это поддержка multi-db, которая немного изменила ситуацию, но даже это было 1.1 или 1.2.

Даже если вам нужна специальная БД для определенных просмотров, я думаю, вам, вероятно, будет лучше определить ее в настройках.

Ответ 6

В SQLAlchemy теперь есть отличная рецензия на то, как вы можете использовать pinging для пессимизма в отношении свечения подключения:

http://docs.sqlalchemy.org/en/latest/core/pooling.html#disconnect-handling-pessimistic

Оттуда,

from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

И тест, чтобы убедиться, что это работает:

from sqlalchemy import create_engine
e = create_engine("mysql://scott:[email protected]/test", echo_pool=True)
c1 = e.connect()
c2 = e.connect()
c3 = e.connect()
c1.close()
c2.close()
c3.close()

# pool size is now three.

print "Restart the server"
raw_input()

for i in xrange(10):
    c = e.connect()
    print c.execute("select 1").fetchall()
    c.close()

Ответ 7

У меня была эта проблема, и у меня не было возможности изменить мою конфигурацию. Наконец, я понял, что проблема связана с записью 49500 записей в мой цикл 50000 записей, потому что это было то время, когда я снова пытался (после того, как давно попробовал) ударить по моей второй базе данных.

Итак, я изменил свой код, чтобы каждые несколько тысяч записей я снова коснулся второй базы данных (со счетом() очень маленькой таблицы) и исправил ее. Без сомнения, "ping" или некоторые другие способы касания базы данных также будут работать.

Ответ 8

По моему мнению, общая проблема, связанная с таким предупреждением, заключается в том, что ваше приложение достигло значения wait_timeout MySQL.

У меня была та же проблема с приложением PythonFLASK, которое я разработал, и я решил очень легко.

P.S: Я использовал MySQL 5.7.14

$ grep timeout /etc/mysql/mysql.conf.d/mysqld.cnf 
# https://support.rackspace.com/how-to/how-to-change-the-mysql-timeout-on-a-server/
# wait = timeout for application session (tdm)
# inteactive = timeout for keyboard session (terminal)
# 7 days = 604800s / 4 hours = 14400s 
wait_timeout = 604800
interactive_timeout = 14400

Одно важное замечание:, если вы ищете переменные через пакетный режим MySQL, значения будут отображаться как есть. Но если вы выполните "SHOW VARIABLES LIKE" wait% '; или "SHOW VARIABLES LIKE" interactive% '; ", значение, настроенное для' interactive_timeout ', будет отображаться для обеих переменных, и я не знаю, почему, но факт в том, что значения, настроенные для каждой переменной, равны'/etc/mysql/mysql.conf.d/mysqld.cnf ', будет соблюдаться процессом MySQL.

Привет!

Ответ 9

Эта ошибка таинственна, потому что MySQL не сообщает, почему она отключается, она просто уходит.

Кажется, есть много причин такого рода разъединения. Я только что нашел, если строка запроса слишком велика, сервер отключится. Это, вероятно, относится к настройке max_allowed_packets.

Ответ 10

Во-первых, вы должны убедиться, что значения сеанса MySQL и глобальные среды wait_timeout и interactive_timeout. А во-вторых, ваш клиент должен попытаться подключиться к серверу ниже тех значений среды.

Ответ 11

Это может быть связано с тем, что соединения с БД копируются в ваши дочерние потоки из основного потока. Я столкнулся с той же ошибкой при использовании многопроцессорной библиотеки Python для порождения различных процессов. Объекты соединения копируются между процессами во время разветвления, и это приводит к MySQL OperationalErrors при выполнении вызовов БД в дочернем потоке.

Вот хорошая ссылка для решения этой проблемы: многопроцессорность Django и соединения с базой данных

Ответ 12

Для меня это происходило в режиме отладки.

Поэтому я попробовал Постоянные соединения в режиме отладки, проверьте ссылку: Django - Документация - Базы данных - Постоянные соединения.

В настройках:

'default': {
       'ENGINE': 'django.db.backends.mysql',
       'NAME': 'dbname',
       'USER': 'root',
       'PASSWORD': 'root',
       'HOST': 'localhost',
       'PORT': '3306',
       'CONN_MAX_AGE': None
    },