it-swarm.asia

Mengelola konkurensi saat menggunakan pola SELECT-UPDATE

Katakanlah Anda memiliki kode berikut (abaikan saja itu buruk):

BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else

Bagi saya, ini TIDAK mengelola konkurensi dengan benar. Hanya karena Anda melakukan transaksi tidak berarti orang lain tidak akan membaca nilai yang sama dengan yang Anda lakukan sebelum Anda mendapatkan pernyataan pembaruan Anda.

Sekarang, biarkan kode apa adanya (saya menyadari ini lebih baik ditangani sebagai satu pernyataan atau bahkan lebih baik menggunakan kolom autoincrement/identitas) apa cara yang pasti untuk membuatnya menangani concurrency dengan benar dan mencegah kondisi balapan yang memungkinkan dua klien untuk mendapatkan yang sama nilai id?

Saya cukup yakin bahwa menambahkan WITH (UPDLOCK, HOLDLOCK) ke SELECT akan membantu. The level isolasi transaksi SERIALIZABLE tampaknya akan berfungsi dengan baik karena ia menolak orang lain untuk membaca apa yang Anda lakukan sampai tran berakhir ( [~ # ~] pembaruan [ ~ # ~] : ini salah. Lihat jawaban Martin). Benarkah? Apakah keduanya akan bekerja dengan baik? Apakah yang satu lebih disukai daripada yang lain?

Bayangkan melakukan sesuatu yang lebih sah daripada pembaruan ID - beberapa perhitungan berdasarkan pembacaan yang perlu Anda perbarui. Mungkin ada banyak tabel yang terlibat, beberapa di antaranya akan Anda tulis dan yang lain tidak. Apa praktik terbaik di sini?

Setelah menulis pertanyaan ini, saya pikir petunjuk kunci lebih baik karena Anda hanya mengunci tabel yang Anda butuhkan, tapi saya menghargai masukan siapa pun.

P.S. Dan tidak, saya tidak tahu jawaban terbaik dan benar-benar ingin mendapatkan pemahaman yang lebih baik! :)

25
ErikE

Hanya menangani aspek level isolasi SERIALIZABLE. Ya ini akan berhasil tetapi dengan risiko jalan buntu.

Dua transaksi akan dapat membaca baris secara bersamaan. Mereka tidak akan memblokir satu sama lain karena mereka akan mengambil objek S mengunci atau indeks RangeS-S kunci bergantung pada struktur tabel dan kunci ini kompatibel . Tetapi mereka akan memblokir satu sama lain ketika mencoba untuk mendapatkan kunci yang diperlukan untuk pembaruan (objek IX mengunci atau indeks RangeS-U masing-masing) yang akan menyebabkan kebuntuan.

Penggunaan petunjuk UPDLOCK secara eksplisit sebagai gantinya akan membuat serial bacaan sehingga menghindari risiko kebuntuan.

11
Martin Smith

Saya pikir pendekatan terbaik bagi Anda adalah dengan benar-benar mengekspos modul Anda ke konkurensi tinggi dan lihat sendiri. Terkadang UPDLOCK saja sudah cukup, dan tidak perlu HOLDLOCK. Terkadang sp_getapplock bekerja dengan sangat baik. Saya tidak akan membuat pernyataan selimut di sini - kadang menambahkan satu indeks lagi, pemicu, atau tampilan indeks mengubah hasilnya. Kita perlu menekankan kode uji dan melihat sendiri berdasarkan kasus per kasus.

Saya telah menulis beberapa contoh pengujian stres di sini

Edit: untuk pengetahuan yang lebih baik tentang internal, Anda dapat membaca buku-buku Kalen Delaney. Namun, buku mungkin tidak sinkron seperti dokumentasi lainnya. Selain itu, ada terlalu banyak kombinasi untuk dipertimbangkan: enam tingkat isolasi, banyak jenis kunci, indeks berkerumun/nonclustered dan siapa yang tahu apa lagi. Itu banyak kombinasi. Selain itu, SQL Server adalah sumber tertutup, jadi kami tidak dapat mengunduh kode sumber, men-debug-nya dan semacamnya - yang akan menjadi sumber utama pengetahuan. Hal lain mungkin tidak lengkap atau ketinggalan jaman setelah rilis berikutnya atau paket layanan.

Jadi, Anda tidak boleh memutuskan apa yang berfungsi untuk sistem Anda tanpa pengujian stres Anda sendiri. Apa pun yang Anda baca, ini dapat membantu Anda memahami apa yang sedang terjadi, tetapi Anda harus membuktikan bahwa saran yang Anda baca bermanfaat untuk Anda. Saya tidak berpikir ada yang bisa melakukannya untuk Anda.

11
A-K

Dalam kasus khusus ini, penambahan UPDLOCK mengunci ke SELECT memang akan mencegah anomali. Penambahan HOLDLOCK tidak diperlukan karena kunci pembaruan ditahan selama transaksi berlangsung, tetapi saya mengaku untuk memasukkannya sendiri sebagai kebiasaan (mungkin buruk) di masa lalu.

Bayangkan melakukan sesuatu yang lebih sah daripada pembaruan ID, beberapa perhitungan berdasarkan pembacaan yang perlu Anda perbarui. Mungkin ada banyak tabel yang terlibat, beberapa di antaranya akan Anda tulis dan yang lain tidak. Apa praktik terbaik di sini?

Tidak ada praktik terbaik. Pilihan Anda untuk kontrol konkurensi harus didasarkan pada persyaratan aplikasi. Beberapa aplikasi/transaksi perlu dieksekusi seolah-olah mereka memiliki kepemilikan eksklusif atas database, menghindari anomali dan ketidakakuratan di semua biaya. Aplikasi/transaksi lain dapat menoleransi beberapa tingkat gangguan dari satu sama lain.

  • Mengambil tingkat stok terbalut (<5, 10+, 50+, 100+) untuk produk di toko web = pembacaan kotor (tidak akurat tidak masalah).
  • Memeriksa dan mengurangi tingkat stok pada checkout toko web itu = baca berulang (kami HARUS memiliki stok sebelum kami menjual, kami HARUS tidak berakhir dengan tingkat stok negatif).
  • Memindahkan uang tunai antara rekening saya sekarang dan tabungan di bank = serializable (jangan salah menghitung atau salah menaruhkan uang saya!).

Edit: @ komentar AlexKuznetsov mendorong saya membaca kembali pertanyaan dan menghapus kesalahan yang sangat jelas dalam jawaban saya. Catatan untuk diri sendiri pada posting larut malam.

9