Deadlock Pembaruan Indeks SQL Server

13

Saya punya 2 pertanyaan yang ketika dijalankan pada saat yang sama menyebabkan jalan buntu.

Kueri 1 - perbarui kolom yang termasuk dalam indeks (index1):

update table1 set column1 = value1 where id = @Id

Mengambil X-Lock pada table1 kemudian mencoba X-Lock pada index1.

Pertanyaan 2:

select columnx, columny, etc from table1 where {some condition}

Mengambil S-Lock pada index1 kemudian mencoba S-Lock pada table1.

Apakah ada cara untuk mencegah kebuntuan dengan tetap mempertahankan pertanyaan yang sama? Sebagai contoh, bisakah saya mengambil X-Lock pada indeks dalam transaksi pembaruan sebelum pembaruan untuk memastikan tabel dan akses indeks berada dalam urutan yang sama - yang seharusnya mencegah kebuntuan?

Tingkat isolasi adalah Komitmen Baca. Baris dan kunci halaman diaktifkan untuk indeks. Ada kemungkinan bahwa catatan yang sama berpartisipasi di kedua kueri - Saya tidak dapat mengetahui dari grafik jalan buntu karena tidak menampilkan parameter.

Grafik kebuntuan

Dale K
sumber

Jawaban:

11

Apakah ada cara untuk mencegah kebuntuan dengan tetap mempertahankan pertanyaan yang sama?

Grafik deadlock menunjukkan bahwa kebuntuan khusus ini adalah kebuntuan konversi yang terkait dengan pencarian bookmark (pencarian RID dalam kasus ini):

Grafik kebuntuan

Sebagaimana dicatat oleh pertanyaan, risiko kebuntuan umum muncul karena permintaan dapat memperoleh kunci yang tidak kompatibel pada sumber daya yang sama dalam pesanan yang berbeda. The SELECTpermintaan kebutuhan untuk mengakses indeks sebelum meja karena lookup RID, sedangkan UPDATEmemodifikasi permintaan meja pertama, maka indeks.

Menghilangkan kebuntuan membutuhkan menghilangkan salah satu bahan kebuntuan. Berikut ini adalah opsi utama:

  1. Hindari pencarian RID dengan membuat penutup indeks nonclustered. Ini mungkin tidak praktis dalam kasus Anda karena SELECTkueri mengembalikan 26 kolom.
  2. Hindari pencarian RID dengan membuat indeks berkerumun. Ini akan melibatkan pembuatan indeks berkerumun di kolom Proposal. Ini patut dipertimbangkan, meskipun tampaknya kolom ini bertipe uniqueidentifier, yang mungkin atau mungkin bukan pilihan yang baik untuk indeks berkerumun, tergantung pada masalah yang lebih luas.
  3. Hindari mengambil kunci bersama saat membaca dengan mengaktifkan opsi READ_COMMITTED_SNAPSHOTatau SNAPSHOTbasis data. Ini akan membutuhkan pengujian yang cermat, terutama sehubungan dengan perilaku memblokir yang dirancang-dalam. Kode pemicu juga akan memerlukan pengujian untuk memastikan logika berkinerja dengan benar.
  4. Hindari mengambil kunci bersama saat membaca dengan menggunakan READ UNCOMMITTEDlevel isolasi untuk SELECTkueri. Semua peringatan yang biasa berlaku.
  5. Hindari eksekusi bersamaan dari dua pertanyaan yang bersangkutan dengan menggunakan kunci aplikasi eksklusif (lihat sp_getapplock ).
  6. Gunakan petunjuk kunci meja untuk menghindari konkurensi. Ini adalah palu yang lebih besar dari opsi 5, karena dapat memengaruhi pertanyaan lain, bukan hanya dua yang diidentifikasi dalam pertanyaan.

Bisakah saya mengambil X-Lock pada indeks dalam transaksi pembaruan sebelum pembaruan untuk memastikan akses tabel dan indeks dalam urutan yang sama

Anda dapat mencoba ini, dengan membungkus pembaruan dalam transaksi eksplisit dan melakukan SELECTdengan XLOCKpetunjuk pada nilai indeks yang tidak tercakup sebelum pembaruan. Ini bergantung pada Anda mengetahui dengan pasti berapa nilai saat ini dalam indeks nonclustered, menjalankan rencana eksekusi dengan benar, dan mengantisipasi dengan benar semua efek samping dari mengambil kunci tambahan ini. Itu juga bergantung pada mesin pengunci yang tidak cukup pintar untuk menghindari pengambilan kunci jika dinilai berlebihan .

Singkatnya, sementara ini pada prinsipnya layak, saya tidak merekomendasikannya. Terlalu mudah untuk melewatkan sesuatu, atau mengakali diri sendiri dengan cara-cara kreatif. Jika Anda benar-benar harus menghindari kebuntuan ini (bukan hanya mendeteksi dan mencoba lagi), saya akan mendorong Anda untuk mencari solusi yang lebih umum yang tercantum di atas.

Paul White 9
sumber
Dari melihat lebih jauh ke dalam masalah saya pikir membiarkannya tidak berubah mungkin yang terbaik. Ini masalah yang lebih umum yang awalnya saya sadari.
Dale K
1

Saya memiliki masalah serupa yang kadang-kadang terjadi dan inilah pendekatan yang saya ambil.

  1. Tambahkan set deadlock priority low;ke pilih. Ini akan menyebabkan kueri ini menjadi korban kebuntuan saat kebuntuan terjadi.
  2. Siapkan retry logic dalam aplikasi Anda untuk secara otomatis mencoba kembali pilih jika gagal karena kebuntuan (atau batas waktu), setelah menunggu / tidur selama periode waktu yang singkat untuk memungkinkan kueri pemblokiran selesai.

Catatan: jika Anda selectadalah bagian dari transaksi multi-pernyataan eksplisit, maka Anda harus yakin untuk mencoba kembali seluruh transaksi, dan bukan hanya pernyataan yang gagal, atau Anda bisa mendapatkan beberapa hasil yang tidak terduga. Jika ini adalah satu selectmaka Anda baik-baik saja, tetapi jika itu adalah pernyataan xdari ndalam transaksi, maka pastikan Anda mencoba kembali semua npernyataan selama coba lagi.

BateTech
sumber
Terima kasih - pertanyaan secara otomatis menemui jalan buntu korban. Dan ya kita sudah memiliki mekanisme coba lagi yang kuat di tempatnya.
Dale K