MySQL: Penyelesaian tetap kepada isu "Menunggu kunci metadata jadual".
P粉076987386
P粉076987386 2023-10-18 21:53:32
0
1
758

Pangkalan data MySQL saya berfungsi sebagai bahagian belakang storan untuk tiga aplikasi web. Walau bagaimanapun, baru-baru ini saya mengalami ralat "Menunggu kunci metadata jadual" secara kekal. Ini berlaku hampir sepanjang masa dan saya tidak faham mengapa.

mysql> show processlist
    -> ;
+------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
| Id   | User      | Host            | db         | Command | Time | State                           | Info                                                                                                 |
+------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+
|   36 | root      | localhost:33444 | bookmaker2 | Sleep   |  139 |                                 | NULL                                                                                                 |
|   37 | root      | localhost:33445 | bookmaker2 | Sleep   |  139 |                                 | NULL                                                                                                 |
|   38 | root      | localhost:33446 | bookmaker2 | Sleep   |  139 |                                 | NULL                                                                                                 |
|   39 | root      | localhost:33447 | bookmaker2 | Sleep   |   49 |                                 | NULL                                                                                                 |
|   40 | root      | localhost:33448 | bookmaker2 | Sleep   |  139 |                                 | NULL                                                                                                 |
| 1315 | bookmaker | localhost:34869 | bookmaker  | Sleep   |   58 |                                 | NULL                                                                                                 |
| 1316 | root      | localhost:34874 | bookmaker3 | Sleep   |   56 |                                 | NULL                                                                                                 |
| 1395 | bookmaker | localhost:34953 | bookmaker  | Sleep   |   58 |                                 | NULL                                                                                                 |
| 1396 | root      | localhost:34954 | bookmaker3 | Sleep   |   46 |                                 | NULL                                                                                                 |
| 1398 | root      | localhost:34956 | bookmaker3 | Query   |   28 | Waiting for table metadata lock | CREATE TABLE IF NOT EXISTS LogEntries  ( 
                    lid         INT NOT NULL AUTO_INCREMEN |
| 1399 | root      | localhost       | NULL       | Query   |    0 | NULL                            | show processlist                                                                                     |
+------+-----------+-----------------+------------+---------+------+---------------------------------+------------------------------------------------------------------------------------------------------+

Sudah tentu anda boleh mematikan proses yang sepadan. Walau bagaimanapun, jika saya memulakan semula program yang cuba mencipta struktur jadual untuk pangkalan data "bookmaker3", proses yang baru dibuat adalah dalam Metallock semula.

Saya tidak boleh memadam pangkalan data:

mysql> drop database bookmaker3;

Ini juga akan mencipta kunci logam.

Bagaimana untuk menyelesaikan masalah ini?

P粉076987386
P粉076987386

membalas semua(1)
P粉647449444

Malangnya, penyelesaian yang diterima adalah salah. Itu betul sekali

Ini pasti (hampir pasti; lihat di bawah) perkara yang perlu dilakukan. Tetapi kemudian ia menunjukkan,

...dan 1398 bukan sambungan kepada kunci. bagaimana begitu? 1398 ialah sambungan yang menunggu untuk dikunci. Ini bermakna ia belum memperoleh kunci lagi, jadi membunuhnya tidak mempunyai kesan. Proses memegang kunci masih akan memegang kunci, dan benang seterusnya yang cuba melakukan sesuatu akan juga berhenti dan masukkan "tunggu kunci metadata" dalam susunan yang sesuai.

Anda tidak boleh menjamin bahawa proses "Menunggu Kunci Metadata" (WFML) tidak akan disekat juga, tetapi anda boleh yakin bahawa hanya mematikan proses WFML akan tidak melakukan apa-apa .

Sebab sebenar ialah proses lain adalah memegang kunci, dan yang lebih penting, SHOW FULL PROCESSLIST tidak akan memberitahu anda secara langsung proses yang mana .

Satu perkara yang anda boleh pasti ialah tiada proses bertanda "Menunggu kunci metadata". Boleh dikatakan mereka ini adalah mangsa.

SHOW FULL PROCESSLIST WILL memberitahu anda jika sesuatu proses sedang melakukan sesuatu, ya. Biasanya ia akan berfungsi. Di sini, proses memegang kunci tidak melakukan apa-apa, dan tersembunyi dalam benang lain yang juga tidak melakukan apa-apa dan dilaporkan sebagai "tidur".

Jika SHOW FULL PROCESSLISTmenunjukkan kepada anda proses yang menjalankan DML, atau dalam keadaan "menghantar data", maka itu hampir pasti puncanya. Proses lain sedang menunggu untuk ia melepaskan kunci (ia boleh menjadi kunci tersirat; proses itu tidak perlu mengeluarkan LOCK TABLE sama sekali, yang sebenarnya mengunci dengan cara yang berbeza). Tetapi proses boleh memegang kunci semasa tidak menjalankan sebarang operasi dan ditandakan dengan sewajarnya sebagai "tidur".

Dalam kes OP, pelakunya hampir pasti proses 1396, yang dimulakan sebelum proses 1398, kini berada dalam keadaan 睡眠, dan telah berlangsung selama 46 saat. Memandangkan 1396 nampaknya telah melakukan semua yang perlu dilakukan (ternyata ia sedang tidur sekarang, dan telah melakukannya selama 46 saat, setakat MySQL yang berkenaan), tiada benang yang masuk masuk ia boleh memegang kunci dan masih tahan Biarkan ia tidur sebelum (jika tidak 1396 juga akan berhenti).

Disebabkan dasar penguncian "tanpa jalan buntu" MySQL, tiada proses boleh menahan kunci, melepaskan kunci, dan memulihkan kunci itu semula, oleh itu, menunggu kunci sentiasa disebabkan oleh proses yang masih memegang kunci dan tidak pernah memegangnya sebelum ini . Ini berguna (kami akan mengeksploitasi fakta ini di bawah) kerana ia menjamin bahawa kunci "baris gilir" diperintahkan.

Penting: Jika anda menyambung ke MySQL sebagai pengguna terhad, SHOW FULL PROCESSLIST akan tidak menunjukkan semua proses. Jadi kunci mungkin dipegang oleh proses yang anda tidak nampak.

Jadi: Jika SHOW FULL PROCESSLISTmenunjukkan segala-galanya kepada anda dan menunjukkan berjalanproses, maka proses itu mungkin bertanggungjawab dan anda perlu menunggu untuk menyelesaikan apa sahaja yang dilakukannya (atau anda boleh membunuhnya - atas risiko anda sendiri).

Selebihnya jawapan ini berkaitan dengan situasi yang mengelirukan di mana proses sedang menunggu tanpa sebab yang jelas dan tiada siapa yang kelihatan melakukan apa-apa.

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

Perkara di atas boleh dilaraskan untuk hanya menunjukkan proses dalam TIDUR, dan ia akan menyusunnya mengikut urutan masa menurun, jadi lebih mudah untuk mencari proses yang digantung (yang, disebabkan oleh pesanan, biasanya sejurus sebelum "menunggu kunci metadata" Tidur satu; dan ia sentiasa satu daripada lebih banyak tidur

daripada sebarang masa menunggu.

Perkara penting

Simpan sebarang proses "menunggu kunci metadata" asingkan.

Penyelesaian cepat dan kotor, tidak sangat disyorkan, tetapi cepat

Bunuh semua proses dalam keadaan "tidur" pada pangkalan data yang sama yang lebih tua daripada yang tertua benang dalam keadaan "menunggu kunci metadata". Inilah yang Arnaud Amaury akan lakukan:

  • Untuk setiap pangkalan data dengan sekurang-kurangnya satu utas dalam WaitingForMetadataLock:
    • Sambungan tertua dalam WFML pada pangkalan data ini berumur Z saat
    • Semua utas "tidur" pada pangkalan data ini yang lebih tua daripada Z mesti mati. Mulakan dengan yang paling segar, untuk berjaga-jaga.
    • Jika terdapat sambungan lama tidak tidur pada pangkalan data itu, mungkin ia yang memegang kunci, tetapi ia sedang melakukan sesuatu . Anda sudah tentu boleh membunuhnya, tetapi terutamanya jika ia adalah kemas kini/masukkan/padam, lakukannya atas risiko anda sendiri.
    • Selepas setiap KILLBUNUH, nilai semula keadaan dan mulakan semula proses dengan sewajarnya. Proses menunggu mungkin berjalan sekarang, atau mungkin telah berjalan sebentar dan kini sedang tidur. Mereka mungkin memegang kunci metadata baharu sekarang.

Sembilan puluh sembilan kali daripada seratus, benang yang akan dibunuh ialah benang termuda yang sedang tidur dan lebih tua daripada benang lama menunggu kunci metadata:

TIME     STATUS
319      Sleep
205      Sleep
 19      Sleep                      

(*) Pesanan TIME sebenarnya mempunyai milisaat, atau saya diberitahu, ia tidak menunjukkannya. Oleh itu, sementara kedua-dua proses mempunyai nilai masa 19, proses terendah harus lebih muda.

Pembaikan lebih fokus

Jalankan SHOW ENGINE INNODB STATUSTUNJUK STATUS INNODB ENJIN dan lihat bahagian "TRANSAKSI". Antara lain, anda akan menemui 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 menggunakan SHOW FULL PROCESSLIST untuk menyemak apakah thread id 1396 lakukan dengan transaksinya #1701. Ia berkemungkinan besar dalam keadaan "tidur". Jadi: transaksi aktif (#1701) dengan kunci aktif, ia juga membuat beberapa perubahan kerana ia mempunyai entri log buat asal... tetapi sedang melahu. Ini ialah benang yang perlu anda bunuh. Perubahan ini hilang.

Ingat bahawa tidak melakukan apa-apa dalam MySQL tidak bermakna tidak melakukan apa-apa secara umum. Jika anda mendapat beberapa rekod daripada MySQL dan membina CSV untuk muat naik FTP, sambungan MySQL terbiar semasa muat naik FTP.

Sebenarnya, jika proses menggunakan MySQL dan pelayan MySQL berada pada mesin yang sama, yang menjalankan Linux, dan anda mempunyai keistimewaan root, ada cara untuk mengetahui proses yang memiliki kunci sambungan yang diminta. Ini seterusnya membolehkan ia ditentukan (berdasarkan penggunaan CPU, atau paling teruk strace -ff -p pid) sama ada proses itu benar-benar melakukan sesuatu, untuk membantu menentukan sama ada selamat untuk membunuh seseorang.

Mengapa ini berlaku?

Saya telah melihat perkara ini berlaku dengan aplikasi web yang menggunakan sambungan MySQL "berterusan" atau "disatukan", kini biasanya dengan penjimatan masa yang sangat sedikit: contoh aplikasi web ditamatkan, tetapi sambungan tidak , jadi ia dikunci lagi. hidup...dan sekat orang lain.

Satu lagi pendekatan menarik yang saya temui ialah, dalam hipotesis di atas, untuk menjalankan pertanyaan yang mengembalikan beberapa baris, dan mengambil hanya sebahagian daripadanya . Jika pertanyaan tidak ditetapkan kepada "auto bersih" (tetapi DBA yang mendasarinya), ia akan memastikan sambungan terbuka dan menghalang kunci penuh pada meja. Saya mengalami perkara ini dengan sekeping kod yang mengesahkan bahawa baris wujud dengan memilihnya dan mengesahkan sama ada ia mempunyai ralat (ia tidak wujud) atau tidak (ia mesti wujud), tetapi tanpa benar-benar mendapatkan semula baris tersebut.

PHP dan PDO

PDO mempunyai keupayaan sambungan yang berterusan. Beginilah cara saya memastikan PDO tidak mengumpulkan sambungan dan menutup setiap sambungan. sangat bersepah.

Apabila membuka, tetapkan pilihan (pilihan keempat ialah PDO() baharu):

PDO::ATTR_PERSISTENT => false

Apabila terputus sambungan:

// We should have no transactions and no locks.
// So we discard them.
try {
    $pdo->exec('ROLLBACK WORK');
    $pdo->exec('UNLOCK TABLES');
} catch (Exception $err) {
    // Send a mail
}
// No cooperative locks. So this will not hurt a bit.
try {
    $pdo->exec('DO RELEASE_ALL_LOCKS()');
} catch (Exception $err) {
    // Send a mail
}
// Ensure the connection withers on the vine, but not too soon.
$pdo->exec('SET wait_timeout = 5');

// $pdo->setAttribute(PDO::ATTR_TIMEOUT, 5);
// If nothing else works!
// try {
//     $pdo->exec('KILL CONNECTION_ID()');
// } catch (Exception $err) {
//     // Exception here is expected: "Query execution was interrupted"
// }
// Invoke the garbage collector
$pdo = NULL;

Tanya pangkalan data

Jika anda mempunyai MySQL terkini, tetapi tidak terlalu baru kerana ini akan ditamatkan , cara lain untuk mencari puncanya ialah (sekali lagi anda memerlukan mod maklumat kebenaran)

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

Penyelesaian sebenar, mengambil masa dan kerja

Masalah sering disebabkan oleh seni bina ini:

Apabila aplikasi web ditamatkan atau contoh utas ringan aplikasi web ditamatkan, kolam bekas/sambungan mungkin tidak . Ia adalah bekas yang memastikan sambungan terbuka, jadi jelas sambungan tidak ditutup. Seperti yang dijangkakan, MySQL tidak menganggap operasi selesai.

Jika apl web tidak membersihkannya sendiri (tiada urus niaga ROLLBACKCOMMIT,没有UNLOCK TABLES dsb.), maka apa sahaja yang apl web mula lakukan masih wujud dan mungkin masih menyekat orang lain.

Maka ada dua penyelesaian. Lebih teruk lagi ialah menurunkan tamat masa terbiar. Tetapi teka apa yang berlaku jika anda menunggu terlalu lama antara pertanyaan (harfiah: "Pelayan MySQL telah hilang"). Anda kemudian boleh menggunakan mysql_ping jika tersedia (tidak lama lagi akan ditamatkan. PDO mempunyai penyelesaian . Sebagai alternatif anda boleh menyemak untuk ralat dan buka semula sambungan jika ia berlaku (ini adalah cara Python) . Jadi - untuk yuran prestasi kecil - ia boleh dilakukan

.

Penyelesaian yang lebih baik dan lebih bijak bukanlah semudah itu untuk dilaksanakan. Cuba biarkan skrip dibersihkan dengan sendirinya, pastikan anda mendapatkan semula semua baris atau melepaskan semua sumber pertanyaan, menangkap semua pengecualian dan mengendalikannya dengan betul, atau, jika boleh, langkau sambungan berterusan sepenuhnya. Biarkan setiap kejadian mencipta sambungannya sendiri atau gunakan pemacu kolam pintar (dalam PHP PDO, gunakan PDO::ATTR_PERSISTENT 显式设置为 false).

Sebagai alternatif (seperti dalam PHP), anda boleh meminta pemusnah dan pengendali pengecualian memaksa pembersihan sambungan dengan melakukan atau melancarkan semula transaksi (yang sepatutnya mencukupi), dan mungkin juga mengeluarkan buka kunci jadual eksplisit dan RELEASE_ALL_LOCKS (), atau serahkan sambungan bunuh diri (KILL CONNECTION_ID()) untuk hasil yang baik.

Saya tidak tahu cara untuk menanyakan sumber set hasil sedia ada untuk membebaskannya; satu-satunya cara ialah menyimpan sumber ini ke dalam tatasusunan peribadi.

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan