Kapan menggunakan SELECT… FOR UPDATE?

119

Tolong bantu saya memahami kasus penggunaan di belakang SELECT ... FOR UPDATE.

Pertanyaan 1 : Apakah berikut ini contoh yang baik tentang kapan SELECT ... FOR UPDATEharus 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]
  • 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 roomshingga Utas 1 selesai. Apakah itu benar?

Pertanyaan 2 : Kapan sebaiknya seseorang menggunakan SERIALIZABLEisolasi transaksi versus READ_COMMITTEDdengan SELECT ... FOR UPDATE?

Jawaban diharapkan portabel (tidak spesifik database). Jika tidak memungkinkan, jelaskan alasannya.

Gili
sumber
2
RDBMS mana yang Anda gunakan?
Quassnoi
2
@Quassnoi, seperti yang disebutkan di bagian bawah pertanyaan, saya mencari solusi portabel (bukan khusus database).
Gili
2
Apakah ada opsi REPEATABLE_READdan READ_COMMITTEDbahkan opsi portabel? Satu-satunya hasil yang saya dapatkan untuk itu adalah untuk server MSSQL
Billy ONeal
3
@BillyONeal: perhatikan bahwa mode isolasi menjamin bahwa Anda tidak melihat kebiasaan yang tidak mereka izinkan, tetapi jangan katakan apa pun tentang kebiasaan yang mereka izinkan. Ini berarti bahwa pengaturan, katakanlah, READ COMMITTEDmode 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.
Quassnoi
3
A select ... for updatepada roomsmasih memungkinkan room_tagsuntuk dihapus karena mereka adalah tabel terpisah. Apakah Anda bermaksud menanyakan apakah for updateklausul akan mencegah penghapusan dari rooms?
Chris Saxon

Jawaban:

84

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 UPDATEsecara eksplisit.


Untuk mengatasi masalah ini, Utas 1 harus SELECT id FROM rooms FOR UPDATE, dengan demikian mencegah Utas 2 dihapus dari roomshingga Utas 1 selesai. Apakah itu benar?

Ini bergantung pada kontrol konkurensi yang digunakan sistem database Anda.

  • MyISAMdi MySQL(dan beberapa sistem lama lainnya) mengunci seluruh tabel selama kueri.

  • Dalam SQL Server, SELECTkueri menempatkan kunci bersama pada catatan / halaman / tabel yang telah mereka periksa, sementara DMLkueri menempatkan kunci pembaruan (yang kemudian dipromosikan menjadi kunci eksklusif atau diturunkan ke kunci bersama). Kunci eksklusif tidak kompatibel dengan kunci bersama, jadi salah satu SELECTatau DELETEkueri akan terkunci sampai sesi lain dilakukan.

  • Dalam database yang digunakan MVCC(seperti Oracle, PostgreSQL, MySQLdengan InnoDB), sebuah DMLpermintaan membuat salinan dari catatan (dalam satu atau lain cara) dan umumnya pembaca tidak menghalangi para penulis dan sebaliknya. Untuk database ini, a SELECT FOR UPDATEakan berguna: ia akan mengunci salah satu SELECTatau DELETEkueri hingga sesi lain dilakukan, seperti SQL Serverhalnya.

Kapan sebaiknya seseorang menggunakan REPEATABLE_READisolasi transaksi versus READ_COMMITTEDdengan SELECT ... FOR UPDATE?

Umumnya, REPEATABLE READtidak melarang baris bayangan (baris yang muncul atau hilang dalam transaksi lain, daripada diubah)

  • Dalam Oracledan PostgreSQLversi sebelumnya , REPEATABLE READsebenarnya adalah sinonim dari SERIALIZABLE. Pada dasarnya, ini berarti bahwa transaksi tidak melihat perubahan yang dibuat setelah dimulai. Jadi dalam pengaturan ini, Thread 1kueri 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 READdan SERIALIZABLEhal-hal yang berbeda: pembaca dalam SERIALIZABLEmode mengatur kunci tombol berikutnya pada catatan yang mereka evaluasi, secara efektif mencegah terjadinya bersamaan DMLpada mereka. Jadi Anda tidak memerlukan SELECT FOR UPDATEdalam mode serializable, tetapi membutuhkannya dalam REPEATABLE READatau READ 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 MVCCatau sebaliknya).

Ketika saya mengatakan "Anda tidak perlu SELECT FOR UPDATE" Saya benar-benar harus menambahkan "karena efek samping dari implementasi mesin database tertentu".

Quassnoi
sumber
1
Poin terakhir adalah inti dari masalah ini, saya pikir: "Anda tidak memerlukan SELECT FOR UPDATE dalam mode serializable, tetapi membutuhkannya dalam REPEATABLE READ atau READ COMMITED".
Colin 't Hart
Kamu benar. Pertanyaan kedua harus ditanyakan kapan SERIALIZABLEharus digunakan versus READ_COMMITTEDdengan SELECT ... FOR UPDATE. Bisakah Anda memperbarui jawaban Anda untuk mencerminkan pertanyaan yang diperbarui ini?
Gili
1
@Gili: "Anda tidak memerlukan SELECT FOR UPDATEdalam mode serializable", dengan InnoDB. Dengan MVCCsistem lain , keduanya adalah sinonim dan Anda memang membutuhkannya SELECT FOR UPDATE.
Quassnoi
1
Saya menemukan posting Colin menjawab pertanyaan spesifik saya lebih baik daripada jawaban Anda, tetapi saya menghargai semua referensi yang Anda berikan. Saya akan menerima jawaban yang paling baik menggabungkan keduanya (jawaban spesifik di atas, referensi pendukung di bawah).
Gili
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 antara SELECTakhir transaksi. Lantas, bukankah jawabannya hanya Yesdengan referensi pendukung di bawah ini?
Gili
33

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:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Karena baris yang terpengaruh oleh my_conditionterkunci, 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.

Colin 't Hart
sumber
1
Saya pikir secara 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.
Gili
1
Saya menemukan posting Anda menjawab pertanyaan spesifik saya lebih baik daripada Quassnoi tetapi saya menghargai semua referensi yang dia berikan. Saya akan menerima jawaban yang paling baik menggabungkan keduanya (jawaban spesifik di atas, referensi pendukung di bawah).
Gili
Penguncian dan isolasi sangat rumit. Jadi adakah buku untuk mendapatkan pengetahuan tentang itu?
Chao