Berikut adalah catatan saya dari bekerja dengan dukungan MySQL pada masalah penguncian aneh baru-baru ini (versi 5.1.37):
Semua baris dan entri indeks yang dilalui untuk sampai ke baris yang diubah akan dikunci. Tercakup di:
http://dev.mysql.com/doc /refman/5.1/en/innodb-locks-set.html
"Pembacaan penguncian, UPDATE, atau DELETE umumnya menetapkan kunci catatan pada setiap catatan indeks yang dipindai dalam pemrosesan pernyataan SQL. Tidak masalah apakah ada kondisi WHERE dalam pernyataan yang akan mengecualikan baris. InnoDB tidak. tidak ingat kondisi WHERE yang tepat, tetapi hanya tahu rentang indeks mana yang dipindai. ... Jika Anda tidak memiliki indeks yang cocok untuk pernyataan Anda dan MySQL harus memindai seluruh tabel untuk memproses pernyataan, setiap baris tabel menjadi terkunci, yang di turn blokir semua sisipan oleh pengguna lain ke tabel."
Dia. Solusi yang sering membantu adalah dengan melakukan:
UPDATE yang mana saja, setel apa pun ke sesuatu di mana kunci utama masuk (pilih kunci utama dari tabel mana pun di mana batasan diurutkan berdasarkan kunci utama);
Pilihan dalam tidak perlu mengambil kunci dan pembaruan kemudian akan memiliki lebih sedikit pekerjaan yang harus dilakukan untuk pembaruan. Klausa urutan demi memastikan bahwa pembaruan dilakukan dalam urutan kunci utama agar sesuai dengan urutan fisik InnoDB, cara tercepat untuk melakukannya.
Di mana sejumlah besar baris terlibat, seperti dalam kasus Anda, akan lebih baik untuk menyimpan hasil yang dipilih dalam tabel sementara dengan kolom bendera ditambahkan. Kemudian pilih dari tabel sementara di mana flag tidak diatur untuk mendapatkan setiap batch. Jalankan pembaruan dengan batas katakanlah 1000 atau 10000 dan atur flag untuk batch setelah pembaruan. Batas akan menjaga jumlah penguncian ke tingkat yang dapat ditoleransi sementara pekerjaan tertentu hanya perlu dilakukan sekali. Berkomitmen setelah setiap batch untuk melepaskan kunci.
Anda juga dapat mempercepat pekerjaan ini dengan melakukan jumlah tertentu dari kolom yang tidak diindeks sebelum melakukan setiap batch pembaruan. Ini akan memuat halaman data ke kumpulan buffer tanpa mengambil kunci. Kemudian penguncian akan berlangsung untuk jangka waktu yang lebih singkat karena tidak akan ada pembacaan disk.
Ini tidak selalu praktis tetapi ketika itu bisa sangat membantu. Jika Anda tidak dapat melakukannya dalam batch, Anda setidaknya dapat mencoba memilih terlebih dahulu untuk memuat data terlebih dahulu, jika cukup kecil untuk dimasukkan ke dalam kumpulan buffer.
Jika memungkinkan, gunakan mode isolasi transaksi READ COMMITTED. Lihat:
http://dev.mysql.com/doc/refman /5.1/en/set-transaction.html
Untuk mendapatkan pengurangan penguncian tersebut, diperlukan penggunaan logging biner berbasis baris (bukan logging biner berbasis pernyataan default).
Dua masalah yang diketahui:
-
Subquery terkadang kurang optimal. Dalam hal ini adalah subkueri dependen yang tidak diinginkan - saran yang saya buat untuk menggunakan subkueri ternyata tidak membantu dibandingkan dengan alternatif dalam kasus ini karena itu.
-
Penghapusan dan pembaruan tidak memiliki rentang rencana kueri yang sama dengan pernyataan pilihan, jadi terkadang sulit untuk mengoptimalkannya dengan benar tanpa mengukur hasil untuk mengetahui dengan tepat apa yang mereka lakukan.
Kedua hal ini secara bertahap membaik. Bug ini adalah salah satu contoh di mana kami baru saja meningkatkan pengoptimalan yang tersedia untuk pembaruan, meskipun perubahannya signifikan dan masih melalui QA untuk memastikan tidak ada efek buruk yang besar:
http://bugs.mysql.com/bug.php?id=36569