Kesalahan 2006: Server MySQL telah hilang

8

Saya menjalankan aplikasi Python Pyramid di server CentOS menggunakan uWSGI dan nginx. Saya menggunakan SQLAlchemy sebagai ORM, MySQLdb sebagai API, dan MySQL sebagai database. Situs ini belum ditayangkan, jadi satu-satunya lalu lintas adalah saya dan beberapa karyawan perusahaan lainnya. Kami membeli beberapa data untuk mengisi basis data, sehingga tabel terbesar (dan paling sering ditanyakan) adalah ~ 150.000 baris.

Kemarin saya membuka empat tab baru situs web secara berurutan, dan saya mendapatkan kembali beberapa 502 kesalahan Gateway Buruk. Saya mencari di log uWSGI, dan menemukan yang berikut:

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

Catatan penting: Kesalahan ini bukan karena wait_timeout MySQL. Pernah ke sana, melakukan itu.

Saya bertanya-tanya apakah masalah ini disebabkan oleh permintaan bersamaan yang dilayani secara bersamaan. Saya menjadikan diri saya seorang penguji beban orang miskin:

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

Benar saja, dalam sepuluh permintaan itu setidaknya satu akan membuat kesalahan 2006, seringkali lebih. Terkadang kesalahan akan semakin asing, misalnya:

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

Ketika kolom paling pasti ada dan bekerja dengan baik pada semua permintaan identik lainnya. Atau yang ini:

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

Ketika, sekali lagi, itu berfungsi dengan baik untuk semua permintaan lainnya.

Untuk lebih jauh memverifikasi bahwa masalahnya berasal dari koneksi database bersamaan, saya mengatur uWSGI menjadi pekerja tunggal dan multi-threading dinonaktifkan, memaksa permintaan untuk diproses satu per satu. Benar saja, masalahnya hilang.

Dalam upaya untuk menemukan masalah, saya membuat log kesalahan untuk MySQL. Dengan pengecualian beberapa pemberitahuan selama MySQL dimulai, tetap kosong.

Ini konfigurasi MySQL saya:

[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

Googling berat tentang kesalahan itu hanya sedikit mengungkapkan, tetapi menyarankan agar saya meningkatkan max_allowed_packet. Saya meningkatkannya menjadi 100 juta dan memulai kembali MySQL, tetapi itu tidak membantu sama sekali.

Untuk meringkas: Koneksi bersamaan dengan MySQL menyebabkan 2006, 'MySQL server has gone away'dan beberapa kesalahan aneh lainnya. Tidak ada relevansi dalam log kesalahan MySQL.

Saya telah bekerja di ini selama berjam-jam dan belum membuat kemajuan. Bisakah seseorang membantu saya?

Theron Luhn
sumber
Ketika Anda berurusan dengan permintaan bersamaan, apakah setiap utas (atau proses atau apa pun) membuat koneksi sendiri ke database?
DerfK
Setiap proses memiliki kumpulan koneksi yang dikelola oleh SQLAlchemy, sehingga setiap permintaan harus memiliki koneksi sendiri.
Theron Luhn
Catatan lain: Pengujian beban tidak menyebabkan masalah pada server pengembangan lokal saya, yaitu Pelayan untuk server dan MySQL untuk database.
Theron Luhn

Jawaban:

18

Saya juga mengalami ini dan menemukan alasan dan memperbaikinya.

Alasan ini terjadi adalah bahwa plugin python uwsgi (atau lebih mungkin semua plugin uwsgi) fork () pekerja baru setelah aplikasi dimuat di induk. Akibatnya, anak-anak mewarisi semua sumber daya (termasuk deskriptor file seperti koneksi db) dari orang tua.

Anda dapat membaca tentang ini secara singkat di uwsgi wiki :

uWSGI mencoba menyalahgunakan fork () salin saat menulis jika memungkinkan. Secara default itu akan bercabang setelah memuat aplikasi Anda. Jika Anda tidak ingin perilaku itu gunakan opsi --lazy. Mengaktifkannya, akan menginstruksikan uWSGI untuk memuat aplikasi setelah setiap garpu pekerja ()

Dan seperti yang Anda ketahui, koneksi mysqldb dan kursor Python bukan threadsafe kecuali Anda secara eksplisit melindunginya. Oleh karena itu banyak proses (seperti pekerja uwsgi) menggunakan koneksi mysql yang sama / kursor secara bersamaan akan merusaknya.

Dalam kasus saya (untuk API Emas Raja Arthur ) ini berfungsi dengan baik ketika saya membuat koneksi MySQL per-permintaan dalam ruang lingkup modul lain, tetapi ketika saya ingin koneksi terus-menerus untuk membantu dengan kinerja saya memindahkan koneksi database dan kursor ke lingkup global di modul induk. Akibatnya, koneksi saya saling menginjak seperti milik Anda.

Cara mengatasinya adalah dengan menambahkan kata kunci "lazy" (atau - baris perintah command) ke konfigurasi uwsgi Anda. Akibatnya, aplikasi akan bercabang lagi untuk setiap anak alih-alih bercabang dari orang tua dan berbagi koneksi (dan menginjaknya di beberapa titik, sehingga server MySQL memaksa itu ditutup karena permintaan yang korup di beberapa titik).

Terakhir, jika Anda menginginkan cara untuk melakukan ini tanpa mengubah konfigurasi uwsgi Anda, Anda mungkin dapat menggunakan dekorator @postfork untuk membuat koneksi database baru dengan benar segera setelah proses pekerja bercabang dua. Anda dapat membaca tentang itu di sini .

Saya melihat dari tindak lanjut Anda bahwa Anda sudah beralih ke pgsql, tetapi inilah jawabannya sehingga Anda dapat tidur lebih nyenyak di malam hari dan bagi siapa pun seperti Anda dan saya mencoba menemukan jawabannya!

PS Suatu ketika saya memiliki pemahaman tentang masalah (kursor rusak karena pekerja saling menginjak) tetapi tidak menyadari sedikit tentang garpu () dan - lazy, saya sedang mempertimbangkan menerapkan kolam saya sendiri di mana pekerja akan " periksa "koneksi mysql dari kumpulan pada lingkup global, lalu" periksa kembali "tepat sebelum keluar dari aplikasi (), namun sepertinya jauh lebih baik untuk menggunakan --lazy kecuali jika beban web / aplikasi Anda cukup bervariasi sehingga Anda selalu menciptakan pekerja baru. Bahkan kemudian saya mungkin lebih suka - lazy karena jauh lebih bersih daripada menerapkan kolam koneksi db Anda sendiri.

sunting: berikut ini adalah penyelesaian yang lebih menyeluruh dari solusi + masalah ini karena ada kekurangan informasi tentangnya untuk orang lain yang telah mengalaminya: http://tns.u13.net/?p=190

FliesLikeABrick
sumber
Pasti baik untuk mengetahui apa yang menyebabkan ini. Terima kasih!
Theron Luhn
Hanya membuang di sana bahwa posting ini adalah masalah yang persis sama yang saya alami juga dan solusi Anda memperbaikinya :) Terima kasih!
MasterGberry
"Karena itu banyak proses (seperti pekerja uwsgi) yang menggunakan koneksi / kursor mysql yang sama secara bersamaan akan merusaknya." Ini sangat instruktif. Saya memiliki dua koneksi terbuka ke database yang sama pada lokal saya (satu dari shell, yang lain dari aplikasi wsgi saya) dan mendapat kesalahan ini. Basis data melaporkan dirinya hidup ke pingdan mysqladminpermintaan lainnya . Itu mungkin karena saya mencoba untuk menjatuhkan database dari shell ... tetapi terus memberikan kesalahan "server telah hilang" untuk perintah itu. Bagaimanapun, terima kasih!
Brian Peterson
kau telah menyelamatkan hidupku.
lintah