Errore 2006: il server MySQL è andato via


8

Sto eseguendo un'app Python Pyramid su un server CentOS usando uWSGI e nginx. Sto usando SQLAlchemy come ORM, MySQLdb come API e MySQL come database. Il sito non è ancora attivo, quindi l'unico traffico sono io e alcuni altri dipendenti dell'azienda. Abbiamo acquistato alcuni dati per popolare il database, quindi la tabella più grande (e più frequentemente interrogata) è di circa 150.000 righe.

Ieri ho aperto quattro nuove schede del sito Web in rapida successione e ho restituito un paio di errori 502 Bad Gateway. Ho cercato nel registro uWSGI e ho trovato quanto segue:

sqlalchemy.exc.OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 'SELECT ge...

Nota importante: questo errore non è dovuto al wait_timeout di MySQL. Ci sono stato, l'ho fatto.

Mi chiedevo se il problema fosse causato da richieste simultanee che venivano servite contemporaneamente. Mi sono fatto un tester di carico per un uomo povero:

for i in {1..10}; do (curl -o /dev/null http://domain.com &); done;

Abbastanza sicuro, in quelle dieci richieste almeno uno avrebbe lanciato un errore del 2006, spesso di più. A volte gli errori diventano ancora più strani, ad esempio:

sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'table.id'"

Quando la colonna esiste sicuramente e ha funzionato bene su tutte le altre richieste identiche. Oppure, questo:

sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.

Quando, ancora una volta, ha funzionato bene per tutte le altre richieste.

Per verificare ulteriormente che il problema derivasse da connessioni simultanee al database, ho impostato uWSGI su un singolo lavoratore e il multi-threading disabilitato, forzando l'elaborazione delle richieste una alla volta. Abbastanza sicuro, i problemi sono scomparsi.

Nel tentativo di trovare il problema, ho impostato un registro errori per MySQL. Ad eccezione di alcuni avvisi durante l'avvio di MySQL, rimane vuoto.

Ecco la mia configurazione di MySQL:

[mysqld]
default-storage-engine = myisam
key_buffer = 1M
query_cache_size = 1M
query_cache_limit = 128k
max_connections=25
thread_cache=1
skip-innodb
query_cache_min_res_unit=0
tmp_table_size = 1M
max_heap_table_size = 1M
table_cache=256
concurrent_insert=2
max_allowed_packet = 1M
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
innodb_file_per_table=1
log-error=/var/log/mysql/error.log

Il google pesante sull'errore ha rivelato poco, ma mi ha suggerito di aumentare max_allowed_packet. L'ho aumentato a 100 M e ho riavviato MySQL, ma non ha aiutato affatto.

Riassumendo: connessioni simultanee alla causa MySQL 2006, 'MySQL server has gone away'e altri strani errori. Non c'è nulla di rilevante nel registro degli errori di MySQL.

Ci lavoro da ore e non ho fatto progressi. Qualcuno può darmi una mano?


Quando hai a che fare con richieste simultanee, ogni thread (o processo o altro) sta facendo la propria connessione al database?
DerfK,

Ogni processo ha un pool di connessioni gestito da SQLAlchemy, quindi ogni richiesta dovrebbe avere la propria connessione.
Theron Luhn,

Un'altra nota: il test di carico non causa alcun problema sul mio server di sviluppo locale, che è Waitress per il server e MySQL per il database.
Theron Luhn,

Risposte:


18

Ho riscontrato anche questo e ho trovato il motivo e la soluzione.

La ragione per cui ciò accade è che il plugin python uwsgi (o più probabilmente tutti i plugin uwsgi) fork () nuovi lavoratori dopo che l'applicazione è stata caricata nel genitore. Di conseguenza, i figli ereditano tutte le risorse (compresi i descrittori di file come la connessione db) dal genitore.

Puoi leggerlo brevemente sul wiki di uwsgi :

uWSGI tenta di abusare della copia fork () in scrittura ogni volta che è possibile. Per impostazione predefinita, eseguirà il fork dopo aver caricato le applicazioni. Se non si desidera tale comportamento, utilizzare l'opzione --lazy. Abilitandolo, verrà indicato a uWSGI di caricare le applicazioni dopo ogni fork del lavoratore ()

E come forse saprai, le connessioni e i cursori mysqldb di Python non sono sicuri se non li proteggi esplicitamente. Pertanto più processi (come i lavoratori uwsgi) che utilizzano la stessa connessione / cursore mysql contemporaneamente la corromperanno.

Nel mio caso (per l' API Gold di King Arthur ) questo ha funzionato bene quando ho creato la connessione MySQL per richiesta nell'ambito di un altro modulo, ma quando volevo che le connessioni persistenti aiutassero con le prestazioni ho spostato la connessione al database e il cursore sull'ambito globale in il modulo genitore. Di conseguenza, le mie connessioni si stavano facendo strada l'una sull'altra come la tua.

La soluzione a questo è aggiungere la parola chiave "lazy" (o l'opzione --lazy della riga di comando) alla tua configurazione uwsgi. Di conseguenza, l'applicazione verrà di nuovo biforcuta per ogni bambino invece di biforcarsi dal genitore e condividere la connessione (e calpestarla ad un certo punto, in modo tale che il server MySQL la costringa a chiudere a causa di una richiesta corrotta ad un certo punto).

Infine, se volevi un modo per farlo senza modificare la tua configurazione uwsgi, puoi probabilmente usare il decoratore @postfork per creare correttamente una nuova connessione al database immediatamente dopo che un processo di lavoro è stato biforcato. Puoi leggerlo qui .

Dal tuo follow-up vedo che sei già passato a pgsql, ma ecco la risposta per dormire meglio di notte e per chiunque come te e io stiamo cercando di trovare la risposta!

PS Una volta che ho capito il problema (il cursore si è corrotto a causa degli operai che si calpestavano) ma non mi rendevo conto del bit su fork () e --lazy, stavo prendendo in considerazione l'implementazione del mio pool in cui i lavoratori avrebbero " controlla "una connessione mysql da un pool nell'ambito globale, quindi" ricontrolla "appena prima di uscire dall'applicazione (), tuttavia è probabilmente molto meglio usare --lazy a meno che il carico di web / applicazioni non sia abbastanza variabile da essere costantemente creare nuovi lavoratori. Anche allora potrei preferire --lazy perché è significativamente più pulito dell'implementazione del tuo pool di connessioni db.

modifica: ecco un approfondimento più approfondito di questo problema + soluzione in quanto vi è una carenza di informazioni su di esso per gli altri che lo hanno riscontrato: http://tns.u13.net/?p=190


È sicuramente bello sapere cosa stava causando questo. Grazie!
Theron Luhn,

Ho appena detto che questo post era lo stesso problema esatto che stavo riscontrando e la tua soluzione l'ha risolto :) Grazie!
MasterGberry

"Pertanto più processi (come i lavoratori uwsgi) che utilizzano la stessa connessione / cursore mysql contemporaneamente la corromperanno." Questo è stato molto istruttivo. Avevo due connessioni aperte allo stesso database sul mio locale (una da una shell, un'altra dalla mia applicazione wsgi) e ho riscontrato questo errore. Il database si stava riferendo vivo pinge altre mysqladminrichieste. Probabilmente era perché stavo cercando di eliminare il database dalla shell ... ma continuava a dare l'errore "il server è andato via" a quel comando. Comunque grazie!
Brian Peterson,

mi hai salvato la vita.
sanguisuga il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.