Bagaimana cara menerapkan penguncian optimis di MySQL dengan benar?
Tim kami telah menyimpulkan bahwa kami harus melakukan # 4 di bawah ini atau ada risiko bahwa utas lain dapat memperbarui versi catatan yang sama, tetapi kami ingin memvalidasi bahwa ini adalah cara terbaik untuk melakukannya.
- Buat bidang versi pada tabel yang ingin Anda gunakan penguncian optimis untuk misalnya kolom nama = "versi"
- Pada pilihan, pastikan untuk memasukkan kolom versi dan catat versi tersebut
- Pada pembaruan berikutnya ke catatan, pernyataan pembaruan harus mengeluarkan "di mana versi = X" di mana X adalah versi yang kami terima di # 2 dan mengatur bidang versi selama pernyataan pembaruan itu ke X + 1
- Lakukan
SELECT FOR UPDATE
pada catatan yang akan kami perbarui sehingga kami membuat serialisasi siapa yang dapat membuat perubahan pada catatan yang kami coba perbarui.
Untuk memperjelas, kami mencoba untuk mencegah dua utas yang memilih catatan yang sama di jendela waktu yang sama di mana mereka mengambil versi catatan yang sama dari saling menimpa satu sama lain jika mereka mencoba dan memperbarui catatan pada saat yang sama. Kami percaya bahwa kecuali kami # 4, ada kemungkinan, bahwa jika kedua utas memasukkan transaksi masing-masing pada saat yang sama (tetapi belum mengeluarkan pembaruan mereka), ketika mereka pergi untuk memperbarui, utas kedua yang akan menggunakan UPDATE ... di mana versi = X akan beroperasi pada data lama.
Apakah kita benar dalam berpikir bahwa kita harus melakukan penguncian pesimistis ini ketika memperbarui walaupun kita menggunakan bidang versi / penguncian optimis?
SELECT ... FOR UPDATE
atau penguncian optimis demi baris versioning, tidak keduanya. Lihat detail sebagai jawaban.Jawaban:
Pengembang Anda salah. Anda memerlukan salah satu
SELECT ... FOR UPDATE
atau versi baris, bukan keduanya.Cobalah dan lihatlah. Buka tiga sesi MySQL
(A)
,(B)
dan(C)
ke database yang sama.Dalam
(C)
masalah:Di keduanya
(A)
dan(B)
keluarkanUPDATE
yang menguji dan menetapkan versi baris, ubahwinner
teks di masing-masing sehingga Anda dapat melihat sesi mana yang:Sekarang
(C)
,UNLOCK TABLES;
untuk melepaskan kunci.(A)
dan(B)
akan berlomba untuk kunci baris. Salah satunya akan menang dan mendapatkan kunci. Yang lain akan memblokir kunci. Pemenang yang mendapatkan kunci akan melanjutkan untuk mengubah baris. Dengan asumsi(A)
adalah pemenang, Anda sekarang dapat melihat baris yang diubah (masih belum dikomit sehingga tidak terlihat oleh transaksi lain) dengan aSELECT * FROM test WHERE id = 1
.Sekarang
COMMIT
di sesi pemenang, katakanlah(A)
.(B)
akan mendapatkan kunci dan melanjutkan dengan pembaruan. Namun, versi tidak lagi cocok, sehingga tidak akan mengubah baris, seperti yang dilaporkan oleh hasil penghitungan baris. Hanya satu yangUPDATE
memiliki efek, dan aplikasi klien dapat dengan jelas melihat mana yangUPDATE
berhasil dan yang gagal. Tidak diperlukan penguncian lebih lanjut.Lihat log sesi di pastebin di sini . Saya menggunakan
mysql --prompt="A> "
dll untuk memudahkan membedakan antara sesi. Saya menyalin dan menempelkan output yang disisipkan dalam urutan waktu, jadi ini bukan output yang sepenuhnya mentah dan mungkin saya bisa membuat kesalahan saat menyalin dan menempelkannya. Uji sendiri untuk melihat.Jika Anda belum menambahkan bidang versi baris, maka Anda harus
SELECT ... FOR UPDATE
dapat memastikan pemesanan.Jika Anda memikirkannya, a benar
SELECT ... FOR UPDATE
- benar berlebihan jika Anda segera melakukanUPDATE
tanpa menggunakan kembali data dariSELECT
, atau jika Anda menggunakan versi baris. TheUPDATE
akan mengambil kunci pula. Jika orang lain memperbarui baris antara tulisan Anda dan tulisan selanjutnya, versi Anda tidak akan cocok lagi sehingga pembaruan Anda akan gagal. Begitulah cara penguncian optimis.Tujuannya
SELECT ... FOR UPDATE
adalah:SERIALIZABLE
isolasi atau versi baris.Anda tidak perlu menggunakan penguncian optimis (versi baris) dan
SELECT ... FOR UPDATE
. Gunakan satu atau yang lain.sumber
Tidak diperlukan Kunci (bukan meja, bukan transaksi), atau bahkan yang diinginkan:
sumber