Tolong bantu saya memahami kasus penggunaan di belakang SELECT ... FOR UPDATE
.
Pertanyaan 1 : Apakah berikut ini contoh yang baik tentang kapan SELECT ... FOR UPDATE
harus digunakan?
Diberikan:
- ruangan [id]
- tag [id, nama]
- room_tags [room_id, tag_id]
- room_id dan tag_id adalah kunci asing
Aplikasi ingin mencantumkan semua ruang dan tag-nya, tetapi perlu membedakan antara ruang tanpa tag versus ruang yang telah dihapus. Jika SELECT ... FOR UPDATE tidak digunakan, yang mungkin terjadi adalah:
- Mulanya:
- ruangan berisi
[id = 1]
- tag berisi
[id = 1, name = 'cats']
- room_tags berisi
[room_id = 1, tag_id = 1]
- ruangan berisi
- Benang 1:
SELECT id FROM rooms;
returns [id = 1]
- Benang 2:
DELETE FROM room_tags WHERE room_id = 1;
- Benang 2:
DELETE FROM rooms WHERE id = 1;
- Thread 2: [melakukan transaksi]
- Benang 1:
SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
- mengembalikan daftar kosong
Sekarang Thread 1 mengira bahwa ruangan 1 tidak memiliki tag, tetapi kenyataannya ruangan tersebut telah dihapus. Untuk mengatasi masalah ini, Utas 1 harus SELECT id FROM rooms FOR UPDATE
, dengan demikian mencegah Utas 2 dihapus dari rooms
hingga Utas 1 selesai. Apakah itu benar?
Pertanyaan 2 : Kapan sebaiknya seseorang menggunakan SERIALIZABLE
isolasi transaksi versus READ_COMMITTED
dengan SELECT ... FOR UPDATE
?
Jawaban diharapkan portabel (tidak spesifik database). Jika tidak memungkinkan, jelaskan alasannya.
REPEATABLE_READ
danREAD_COMMITTED
bahkan opsi portabel? Satu-satunya hasil yang saya dapatkan untuk itu adalah untuk server MSSQLREAD COMMITTED
mode tidak menentukan apakah Anda benar-benar akan melihat catatan yang dilakukan oleh transaksi lain: ini hanya memastikan Anda tidak akan pernah melihat catatan yang tidak terikat.select ... for update
padarooms
masih memungkinkanroom_tags
untuk dihapus karena mereka adalah tabel terpisah. Apakah Anda bermaksud menanyakan apakahfor update
klausul akan mencegah penghapusan darirooms
?Jawaban:
Satu-satunya cara portabel untuk mencapai konsistensi antara ruang dan label serta memastikan ruang tidak pernah dikembalikan setelah dihapus adalah dengan menguncinya
SELECT FOR UPDATE
.Namun di beberapa sistem penguncian adalah efek samping dari kontrol konkurensi, dan Anda mencapai hasil yang sama tanpa menentukan
FOR UPDATE
secara eksplisit.Ini bergantung pada kontrol konkurensi yang digunakan sistem database Anda.
MyISAM
diMySQL
(dan beberapa sistem lama lainnya) mengunci seluruh tabel selama kueri.Dalam
SQL Server
,SELECT
kueri menempatkan kunci bersama pada catatan / halaman / tabel yang telah mereka periksa, sementaraDML
kueri menempatkan kunci pembaruan (yang kemudian dipromosikan menjadi kunci eksklusif atau diturunkan ke kunci bersama). Kunci eksklusif tidak kompatibel dengan kunci bersama, jadi salah satuSELECT
atauDELETE
kueri akan terkunci sampai sesi lain dilakukan.Dalam database yang digunakan
MVCC
(sepertiOracle
,PostgreSQL
,MySQL
denganInnoDB
), sebuahDML
permintaan membuat salinan dari catatan (dalam satu atau lain cara) dan umumnya pembaca tidak menghalangi para penulis dan sebaliknya. Untuk database ini, aSELECT FOR UPDATE
akan berguna: ia akan mengunci salah satuSELECT
atauDELETE
kueri hingga sesi lain dilakukan, sepertiSQL Server
halnya.Umumnya,
REPEATABLE READ
tidak melarang baris bayangan (baris yang muncul atau hilang dalam transaksi lain, daripada diubah)Dalam
Oracle
danPostgreSQL
versi sebelumnya ,REPEATABLE READ
sebenarnya adalah sinonim dariSERIALIZABLE
. Pada dasarnya, ini berarti bahwa transaksi tidak melihat perubahan yang dibuat setelah dimulai. Jadi dalam pengaturan ini,Thread 1
kueri terakhir akan mengembalikan ruangan seolah-olah belum pernah dihapus (yang mungkin atau mungkin tidak sesuai dengan keinginan Anda). Jika Anda tidak ingin menampilkan kamar setelah dihapus, Anda harus mengunci baris denganSELECT FOR UPDATE
Dalam
InnoDB
,REPEATABLE READ
danSERIALIZABLE
hal-hal yang berbeda: pembaca dalamSERIALIZABLE
mode mengatur kunci tombol berikutnya pada catatan yang mereka evaluasi, secara efektif mencegah terjadinya bersamaanDML
pada mereka. Jadi Anda tidak memerlukanSELECT FOR UPDATE
dalam mode serializable, tetapi membutuhkannya dalamREPEATABLE READ
atauREAD COMMITED
.Perhatikan bahwa standar pada mode isolasi tidak menentukan bahwa Anda tidak melihat kebiasaan tertentu dalam kueri Anda tetapi tidak menentukan bagaimana (dengan mengunci atau dengan
MVCC
atau sebaliknya).Ketika saya mengatakan "Anda tidak perlu
SELECT FOR UPDATE
" Saya benar-benar harus menambahkan "karena efek samping dari implementasi mesin database tertentu".sumber
SERIALIZABLE
harus digunakan versusREAD_COMMITTED
denganSELECT ... FOR UPDATE
. Bisakah Anda memperbarui jawaban Anda untuk mencerminkan pertanyaan yang diperbarui ini?SELECT FOR UPDATE
dalam mode serializable", denganInnoDB
. DenganMVCC
sistem lain , keduanya adalah sinonim dan Anda memang membutuhkannyaSELECT FOR UPDATE
.This depends on the concurrency control your database system is using
: Saya pikir Anda sedang membelah rambut. Semua kasus yang Anda cantumkan di bawah mengatakan bahwa ruangan tidak dihapus antaraSELECT
akhir transaksi. Lantas, bukankah jawabannya hanyaYes
dengan referensi pendukung di bawah ini?Jawaban pendek:
Q1: Ya.
Q2: Tidak masalah yang Anda gunakan.
Jawaban panjang:
Kehendak
select ... for update
(seperti yang tersirat) memilih baris tertentu tetapi juga menguncinya seolah-olah mereka telah diperbarui oleh transaksi saat ini (atau seolah-olah pembaruan identitas telah dilakukan). Hal ini memungkinkan Anda untuk memperbaruinya lagi dalam transaksi saat ini dan kemudian melakukan, tanpa transaksi lain yang dapat mengubah baris ini dengan cara apa pun.Cara lain untuk melihatnya, seolah-olah dua pernyataan berikut dijalankan secara atomik:
Karena baris yang terpengaruh oleh
my_condition
terkunci, tidak ada transaksi lain yang dapat mengubahnya dengan cara apa pun, dan karenanya, tingkat isolasi transaksi tidak membuat perbedaan di sini.Perhatikan juga bahwa tingkat isolasi transaksi tidak bergantung pada penguncian: menyetel tingkat isolasi yang berbeda tidak memungkinkan Anda untuk berkeliling mengunci dan memperbarui baris dalam transaksi berbeda yang dikunci oleh transaksi Anda.
Tingkat isolasi transaksi yang menjamin (pada tingkat yang berbeda) adalah konsistensi data saat transaksi sedang berlangsung.
sumber
What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.
tidak benar menyiratkan bahwa tingkat isolasi tidak memengaruhi apa yang terjadi selama transaksi. Saya sarankan untuk merevisi bagian ini dan memberikan lebih banyak detail tentang bagaimana pengaruhnya terhadap apa yang Anda lihat (atau tidak lihat) selama transaksi.