Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

MySQL:Secara permanen mendapatkan kunci metadata tabel Menunggu

Solusi yang diterima, sayangnya, salah . Itu benar sejauh yang dikatakan,

Ini memang (hampir pasti; lihat di bawah) apa yang harus dilakukan. Tapi kemudian itu menyarankan,

...dan 1398 bukan sambungan dengan kunci. Bagaimana mungkin? 1398 adalah koneksi menunggu untuk kunci. Artinya belum ada kunci, dan karena itu, membunuhnya tidak ada gunanya. Proses menahan gembok akan tetap menahan gembok, dan berikutnya utas mencoba melakukan sesuatu karena itu akan juga berhenti dan masukkan "Menunggu kunci metadata" secara berurutan.

Anda tidak memiliki jaminan bahwa proses "menunggu kunci metadata" (WFML) juga tidak akan memblokir, tetapi Anda dapat yakin bahwa mematikan hanya proses WFML akan menghasilkan tidak ada persisnya. .

Penyebab sebenarnya adalah proses lain menahan kunci , dan yang lebih penting, SHOW FULL PROCESSLIST tidak akan memberi tahu Anda secara langsung yang mana .

Itu AKAN memberi tahu Anda jika prosesnya berhasil sesuatu, ya. Biasanya berhasil. Di sini, proses menahan kunci tidak melakukan apa-apa , dan bersembunyi di antara utas lainnya juga tidak melakukan apa-apa.

Dalam hal ini pelakunya hampir pasti proses 1396 , yang dimulai sebelum proses 1398 dan sekarang dalam Sleep negara, dan telah selama 46 detik. Sejak 1396 dengan jelas melakukan semua yang perlu dilakukan (sebagaimana dibuktikan oleh fakta bahwa sekarang sedang tidur, dan telah melakukannya selama 46 detik, sejauh menyangkut MySQL ), tidak ada utas yang tertidur sebelumnya yang dapat mengunci (atau 1396 juga akan terhenti).

PENTING :jika Anda terhubung ke MySQL sebagai pengguna terbatas, SHOW FULL PROCESSLIST akan tidak menunjukkan semua proses. Jadi kuncinya mungkin dipegang oleh proses yang tidak Anda lihat.

SHOW PROCESSLIST yang lebih baik

SELECT ID, TIME, USER, HOST, DB, COMMAND, STATE, INFO
    FROM INFORMATION_SCHEMA.PROCESSLIST WHERE DB IS NOT NULL
    AND (`INFO` NOT LIKE '%INFORMATION_SCHEMA%' OR INFO IS NULL)
    ORDER BY `DB`, `TIME` DESC

Di atas dapat disetel untuk hanya menampilkan proses dalam status SLEEP, dan bagaimanapun itu akan mengurutkannya berdasarkan waktu turun, sehingga lebih mudah untuk menemukan proses yang menggantung (biasanya adalah Sleep 'ing satu segera sebelum yang "menunggu kunci metadata").

Yang penting

Biarkan proses "menunggu kunci metadata" sendirian .

Solusi cepat dan kotor, tidak terlalu direkomendasikan tapi cepat

Bunuh semua proses dalam status "Tidur", pada database yang sama, yang lebih tua dari yang terlama utas dalam status "menunggu kunci metadata". Inilah yang Arnaud Amaury akan dilakukan:

  • untuk setiap database yang memiliki setidaknya satu thread di WaitingForMetadataLock:
    • koneksi tertua di WFML pada DB itu ternyata berumur Z detik
    • SEMUA utas "Tidur" pada DB itu dan yang lebih lama dari Z harus pergi. Mulailah dengan yang paling segar, untuk berjaga-jaga.
    • Jika ada satu koneksi lama dan non-tidur pada DB itu, maka mungkin itu yang memegang kunci, tetapi melakukan sesuatu . Anda tentu saja dapat membunuhnya, tetapi terutama jika itu adalah UPDATE/INSERT/DELETE, Anda melakukannya dengan risiko Anda sendiri.

Sembilan puluh sembilan kali dari seratus, utas yang akan dibunuh adalah yang paling muda di antara mereka yang dalam keadaan Tidur yang lebih tua daripada yang lama menunggu kunci metadata:

TIME     STATUS
319      Sleep
205      Sleep
 19      Sleep                      <--- one of these two "19"
 19      Sleep                      <--- and probably this one(*)
 15      Waiting for metadata lock  <--- oldest WFML
 15      Waiting for metadata lock
 14      Waiting for metadata lock

(*) urutan TIME sebenarnya memiliki milidetik, atau jadi saya diberitahu, itu tidak menunjukkannya. Jadi sementara kedua proses memiliki nilai Waktu 19, yang terendah harus lebih muda.

Perbaikan yang lebih fokus

Jalankan SHOW ENGINE INNODB STATUS dan lihat bagian "TRANSAKSI". Anda akan menemukan, antara lain, sesuatu seperti

TRANSACTION 1701, ACTIVE 58 sec;2 lock struct(s), heap size 376, 1 row lock(s), undo log entries 1
MySQL thread id 1396, OS thread handle 0x7fd06d675700, query id 1138 hostname 1.2.3.4 whatever;

Sekarang Anda memeriksa dengan SHOW FULL PROCESSLIST apa yang dilakukan thread id 1396 dengan transaksi #1701-nya. Kemungkinan dalam status "Tidur". Jadi:transaksi aktif (#1701) dengan kunci aktif, bahkan telah melakukan beberapa perubahan karena memiliki entri log undo... tetapi saat ini tidak digunakan. Itu dan tidak ada yang lain adalah utas yang perlu Anda bunuh. Kehilangan perubahan itu.

Ingat bahwa tidak melakukan apa-apa di MySQL tidak berarti tidak melakukan apa-apa secara umum. Jika Anda mendapatkan beberapa catatan dari MySQL dan membuat CSV untuk unggahan FTP, selama unggahan FTP, koneksi MySQL tidak aktif.

Sebenarnya jika proses menggunakan MySQL dan server MySQL berada di mesin yang sama, mesin itu menjalankan Linux, dan Anda memiliki hak akses root, ada cara untuk mengetahui proses mana memiliki koneksi yang meminta kunci. Ini pada gilirannya memungkinkan untuk menentukan (dari penggunaan CPU atau, paling buruk, strace -ff -p pid ) apakah proses itu benar-benar melakukan sesuatu atau tidak, untuk membantu memutuskan apakah membunuh itu aman.

Mengapa ini terjadi?

Saya melihat ini terjadi pada aplikasi web yang menggunakan koneksi MySQL "persisten" atau "kumpulan", yang saat ini biasanya menghemat sedikit waktu:instance webapp dihentikan, tetapi koneksi tidak , jadi kuncinya masih hidup... dan memblokir semua orang.

Cara menarik lainnya yang saya temukan adalah, dalam hipotesis di atas, untuk menjalankan kueri yang mengembalikan beberapa baris, dan hanya mengambil beberapa di antaranya . Jika kueri tidak disetel ke "pembersihan otomatis" (namun DBA yang mendasari melakukannya), itu akan membuat koneksi tetap terbuka dan mencegah kunci penuh pada tabel masuk. Saya mengalami ini dalam sepotong kode yang memverifikasi apakah ada baris dengan memilih baris itu dan memverifikasi apakah ada kesalahan (tidak ada) atau tidak (harus ada), tetapi tanpa benar-benar mengambil baris .

Tanyakan ke DB

Cara lain untuk mendapatkan pelakunya jika Anda memiliki MySQL baru-baru ini, tetapi tidak terlalu baru karena ini tidak akan digunakan lagi , adalah (Anda memerlukan hak istimewa lagi pada skema informasi)

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS 
     WHERE LOCK_TRX_ID IN 
        (SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);

Solusi aktual, membutuhkan waktu dan kerja

Masalahnya biasanya disebabkan oleh arsitektur ini:

Saat webapp mati, atau instance thread ringan webapp mati, kolam penampung/koneksi mungkin tidak . Dan itu adalah wadah yang membuat koneksi tetap terbuka, jadi jelas koneksi tidak menutup. Dapat diduga, MySQL tidak menganggap operasi selesai .

Jika aplikasi web tidak membersihkan dirinya sendiri (tidak ada ROLLBACK atau COMMIT untuk transaksi, tidak ada UNLOCK TABLES , dll.), maka apa pun yang mulai dilakukan aplikasi web itu masih ada , dan mungkin masih memblokir orang lain.

Kemudian ada dua solusi. Yang lebih buruk adalah menurunkan waktu tunggu idle . Tapi coba tebak apa yang terjadi jika Anda menunggu terlalu lama di antara dua kueri (tepatnya:"server MySQL telah hilang"). Anda kemudian dapat menggunakan mysql_ping jika tersedia (segera dihentikan. Ada solusi untuk PDO. Atau Anda dapat memeriksa itu kesalahan, dan buka kembali koneksi jika itu terjadi (ini adalah cara Python). Jadi - dengan sedikit biaya kinerja - itu bisa dilakukan.

Solusi yang lebih baik dan lebih cerdas kurang mudah diterapkan. Usahakan agar skrip tetap bersih, memastikan untuk mengambil semua baris atau membebaskan semua sumber daya kueri, menangkap semua pengecualian dan menanganinya dengan benar, atau, jika mungkin, lewati koneksi persisten sama sekali . Biarkan setiap instance membuat koneksinya sendiri atau gunakan smart pengemudi kolam renang (di PHP PDO, gunakan PDO::ATTR_PERSISTENT secara eksplisit disetel ke false ). Alternatifnya (misalnya dalam PHP) Anda dapat meminta destruct dan exception handler membersihkan koneksi dengan melakukan atau memutar kembali transaksi dan mengeluarkan buka kunci tabel eksplisit.

Saya tidak tahu cara menanyakan sumber daya hasil yang masih ada untuk membebaskannya; satu-satunya cara adalah menyimpan sumber daya tersebut dalam larik pribadi.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. php - Mengaktifkan pengguna ke posting favorit

  2. akses ditolak untuk memuat infile data di MySQL

  3. Hubungkan Dengan Setara Sebelumnya untuk MySQL

  4. Jumlah perkalian kolom untuk baris dengan ID serupa di MySQL

  5. LocalDateTime , ZonedDateTime dan Timestamp