2013-01-04 22 views
23

Originale: Recentemente ho iniziato a ottenere MySQL OperationalErrors da alcuni dei miei vecchi codici e non riesco a rintracciare il problema. Dato che funzionava prima, pensavo che potesse trattarsi di un aggiornamento software che ha rotto qualcosa. Sto usando Python 2.7 con django runfcgi con nginx. Qui è il mio codice originale:Python e Django OperationalError (2006, 'server MySQL è andato via')

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() 

Ho provato quanto segue, ma ancora non funziona:

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() 

Attualmente, la mia unica soluzione è eseguire MySQLdb.connect() in ciascuna funzione che utilizza mysql. Inoltre, ho notato che quando si utilizzava il django manage.py runserver, non avrei questo problema mentre nginx lanciava questi errori. Dubito che sto scadendo con la connessione perché list() viene chiamato entro pochi secondi dall'avvio del server. Ci sono stati aggiornamenti al software che sto usando che potrebbero causare l'interruzione/c'è qualche soluzione per questo?

Modifica: mi sono reso conto che di recente ho scritto un pezzo di middleware per demonizzare una funzione e questa era la causa del problema. Tuttavia, non riesco a capire perché. Ecco il codice per la middle-ware

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) 
+1

Hai una funzione locale chiamata 'lista'? Questa è una cattiva idea – hughdbrown

+0

In alcuni adattatori/librerie di database esiste un'impostazione per riconnettersi automaticamente, in modo simile a come il client mysql gestisce i timeout. Dai un'occhiata se puoi configurarlo. –

risposta

31

Come per la MySQL documentation, il messaggio di errore viene generato quando il cliente non è in grado di inviare una domanda al server, molto probabilmente perché il server stesso ha chiuso la connessione. Nel caso più comune il server chiuderà una connessione inattiva dopo un (predefinito) di 8 ore. Questo è configurabile sul lato server.

Il MySQL documentation fornisce una serie di altre possibili cause che potrebbero essere la pena esaminare per vedere se si adattano alla situazione.

Un'alternativa alla chiamata di connect() in ogni funzione (che potrebbe causare inutilmente la creazione di nuove connessioni) sarebbe di investigare usando il metodo ping() sull'oggetto di connessione; questo verifica la connessione con l'opzione di tentare una riconnessione automatica. Ho faticato a trovare qualche decent documentation per il metodo ping() online, ma la risposta a this question potrebbe essere di aiuto.

Nota: la riconnessione automatica può essere pericolosa quando si gestiscono le transazioni poiché sembra che la riconnessione causi un rollback implicito (e sembra essere il motivo principale per cui autoreconnect non è una funzionalità dell'implementazione di MySQLdb).

+0

Il suggerimento di ping per verificare se la connessione è attiva prima di eseguire una query è considerato un anti-pattern che spreca risorse ed è inaffidabile: https://www.percona.com/blog/2010/05/05/checking-for -a-live-database-connection-considered-harmful/ –

+0

Grazie per il link, Jeff. Sono d'accordo che l'approccio che danno lì (riprova automatica) è migliore. –

0

Quanti anni ha questo codice? Django ha avuto database definiti in impostazioni almeno da .96. Solo l'altra cosa che posso pensare è il supporto multi-db, che ha cambiato le cose un po ', ma anche quello era 1.1 o 1.2.

Anche se è necessario un DB speciale per determinate visualizzazioni, penso che probabilmente sarebbe meglio definirlo nelle impostazioni.

+0

Stava lavorando prima di andare in vacanza una settimana prima di Natale –

1

Verificare se è possibile creare un oggetto di connessione mysql in un thread e quindi utilizzarlo in un altro.

Se è vietato, utilizzare threading.Local per i collegamenti per-thread:

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(...) 
+0

Il cursore è definito sia nel thread stesso (in dispatch.execute) che in views.py. Avrei ricevuto un errore diverso se il thread non poteva accedervi. –

0

SQLAlchemy ha ora una grande write-up su come è possibile utilizzare facendo un rumore metallico di essere pessimisti circa la freschezza della tua connessione:

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

Da lì,

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() 

E un test per rendere sicuro quanto sopra funziona:

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() 
35

A volte se si ee "OperationalError: (2006, 'server MySQL è andato via')", è perché si sta emettendo una query troppo grande. Questo può accadere, ad esempio, se stai memorizzando le tue sessioni in MySQL e stai cercando di mettere qualcosa di veramente grande nella sessione. Per risolvere il problema, è necessario aumentare il valore dell'impostazione max_allowed_packet in MySQL.

Il valore predefinito è 1048576.

Così vedono il valore corrente per l'impostazione predefinita, eseguire il seguente SQL:

select @@max_allowed_packet; 

Per impostare temporaneamente un nuovo valore, eseguire il seguente SQL:

set global max_allowed_packet=10485760; 

Per risolvere il problema in modo permanente, creare un file /etc/my.cnf con almeno il seguente:

[mysqld] 
max_allowed_packet = 16M 

Dopo aver modificato /etc/my.cnf, è necessario riavviare MySQL o riavviare la macchina se non si sa come.

+1

Ha funzionato per me! Grazie –

+0

"riavvia il tuo computer se non sai come." .... "Ciao, IT ... hai provato a spegnerlo e riaccenderlo?" –

0

Ho anche avuto problemi con questo problema. Non mi piace l'idea di aumentare il timeout su mysqlserver. La connessione automatica con CONNECTION_MAX_AGE non funziona come indicato. Purtroppo ho finito con avvolgimento ogni metodo che interroga il database come questo

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) 

Come potete vedere io invece preferisco cattura l'eccezione che il ping il database ogni volta prima di interrogare esso. Perché prendere un'eccezione è un caso raro. Mi aspetterei che django si ricolleghi automaticamente, ma sembra che il problema sia refused.

0

Ho avuto questo problema e non ho avuto la possibilità di modificare la mia configurazione. Alla fine ho capito che il problema si verificava con 49500 record nel mio ciclo da 50000-record, perché quello era il tempo in cui stavo tentando di nuovo (dopo aver provato molto tempo fa) di colpire il mio secondo database.

Così ho cambiato il mio codice in modo che ogni migliaia di record, ho toccato di nuovo il secondo database (con un conteggio() di una tabella molto piccola), e che risolto. Senza dubbio anche il "ping" o altri mezzi per toccare il database funzionerebbero.

0

A mio parere, il problema comune relativo a tale avviso è il fatto che l'applicazione abbia raggiunto il valore wait_timeout di MySQL.

Ho avuto lo stesso problema con un'applicazione PythonFLASK che ho sviluppato e ho risolto molto facilmente.

P.S: stavo usando 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 

Un'osservazione importante: se si cerca le variabili tramite modalità batch MySQL, viene visualizzato il valore così com'è. Ma se esegui "MOSTRA VARIABILI COME 'aspetta%';" oppure "SHOW VARIABLES LIKE 'interactive%';", il valore configurato per 'interactive_timeout', apparirà su entrambe le variabili, e non so perché, ma il fatto è che i valori configurati per ogni variabile in '/ etc /mysql/mysql.conf.d/mysqld.cnf ', sarà rispettato dal processo MySQL.

Saluti!

0

Questo errore è misterioso perché MySQL non segnala il motivo per cui si disconnette, ma va via.

Sembra che ci siano molte cause di questo tipo di disconnessione. Uno che ho appena trovato è, se la stringa di query troppo grande, il server si disconnetterà. Questo probabilmente si riferisce all'impostazione max_allowed_packets.

0

In primo luogo, è necessario assicurarsi che la sessione MySQL & valori globali wait_timeout e valori interactive_timeout. E in secondo luogo il tuo client dovrebbe provare a riconnettersi al server al di sotto di quei valori ambientali.

Problemi correlati