Alasan bagus untuk menggunakan SELECT ... WITH XLOCK?

11

Saya menghadapi beberapa kebuntuan yang terjadi kembali, salah satunya adalah Keylock dan berisi kueri SELECT dengan petunjuk XLOCK yang menjadi korban kebuntuan. Pernyataan lainnya adalah INSERT ke dalam salah satu tabel yang merupakan bagian dari tampilan permintaan pertama.

Melihat:

create view dbo.viewE
 as
    select * from dbo.E  
    where myValue > 13000 

Pilih Kueri:

select * from dbo.viewE with (XLOCK) where A > GETUTCDATE() 

Pernyataan INSERT:

INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())

Tabel yang mendasari dbo.E memegang sekitar 3 juta baris dalam sekitar 20 kolom, beberapa di antaranya ntext.

Mengambil pertanyaan dan mensimulasikannya secara manual dengan dua transaksi, perilaku dapat diproduksi ulang. Perilaku berubah jika XLOCK dihapus dari pilih.

Deadlock Graph:

<deadlock-list>
 <deadlock victim="process222222221">
  <process-list>
   <process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="serializable (4)" xactid="27202256401" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="48" sqlhandle="0x02000000611e4523142b2318c47c87313a9b2ba587ff3130">
        SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()      </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@UICulture nvarchar(5))SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()    </inputbuf>
   </process>
   <process id="process6022222" taskpriority="0" logused="161152" waitresource="KEY: 5:72057604035644444 (cd874c2ba438)" waittime="1370" ownerId="27202248438" transactionguid="0x8de5ccd6eeef67469c6234af59e44ca5" transactionname="DTCXact" lasttranstarted="2015-09-14T16:32:34.767" XDES="0x4aa0bf950" lockMode="RangeI-N" schedulerid="14" kpid="6636" status="suspended" spid="329" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-09-14T16:32:37.300" lastbatchcompleted="2015-09-14T16:32:37.300" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="read uncommitted (1)" xactid="27202248438" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="936" sqlhandle="0x020000004853462f09790a4ddedc0d574c2afa539aef1c0e">
     INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock258b6dc80" mode="X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process6022222" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process222222221" mode="RangeX-X" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock7b145c400" mode="RangeX-X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process222222221" mode="RangeX-X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6022222" mode="RangeI-N" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

Sejauh yang saya pahami, saya melihat kebuntuan KEYLOCK yang pada dasarnya disebabkan oleh kueri indeks yang terbuka yang menggunakan indeks yang tidak bersatu dan berkerumun untuk mengumpulkan nilai yang diperlukan, bukan?

Pertanyaan saya:

  1. Saya tidak dapat membuat indeks penutup karena kolom NTEXT yang diperlukan terlibat. Akankah secara drastis mengurangi jumlah baris membantu di sini?
  2. Apakah ada alasan bagus mengapa saya tidak tahu SELECT dijalankan dengan XLOCK? Apakah kebuntuan juga akan terjadi tanpa XLOCK?
Magier
sumber

Jawaban:

15

Sejauh yang saya pahami, saya melihat kebuntuan KEYLOCK yang pada dasarnya disebabkan oleh kueri indeks yang terbuka yang menggunakan indeks yang tidak bersatu dan berkerumun untuk mengumpulkan nilai yang diperlukan, bukan?

Intinya ya. Operasi baca (pilih) mengakses indeks nonclustered terlebih dahulu, kemudian indeks berkerumun (lookup). Operasi tulis (masukkan) mengakses indeks berkerumun terlebih dahulu, kemudian indeks yang tidak tercakup. Mengakses sumber daya yang sama dalam urutan berbeda dengan memegang kunci yang tidak kompatibel dapat menyebabkan kebuntuan.

Akankah secara drastis mengurangi jumlah baris membantu di sini?

Ini mungkin , karena sumber daya yang lebih sedikit terkunci dan operasi akan cenderung untuk menyelesaikan lebih cepat. Jika itu membantu, itu dapat mengurangi kebuntuan, tetapi kemungkinan besar tidak menghilangkannya (tapi baca terus).

Apakah ada alasan bagus mengapa saya tidak tahu SELECT dijalankan dengan XLOCK?

Tidak juga. Petunjuk penguncian seperti ini sering diperkenalkan oleh orang-orang tanpa pemahaman penuh tentang bagaimana isolasi, penguncian, dan deadlock bekerja, dalam upaya putus asa untuk mengurangi atau menghilangkan masalah.

Apakah kebuntuan juga akan terjadi tanpa XLOCK?

Tidak , jika pilih benar-benar berjalan pada baca tidak terikat isolasi karena kunci yang tidak kompatibel tidak akan diambil (dan ditahan) dalam urutan yang berbeda.

Ya , jika tingkat isolasi penguncian digunakan, dan kunci yang tidak kompatibel diambil dan ditahan dalam urutan yang tidak konsisten, misalnya dibagikan (S) pada yang tidak dikelompokkan, maka S pada pengelompokan saat membaca. Seberapa besar kemungkinan kebuntuan dalam skenario ini tergantung pada berapa banyak kunci yang diambil, dan untuk berapa lama mereka ditahan.

Nasihat

Hal yang sangat menonjol (saat ditinjau) adalah bahwa transaksi yang dipilih berjalan di bawah isolasi serializable . Itu bisa diatur oleh kerangka kerja Anda, atau karena penggunaan DTC (Koordinator Transaksi Terdistribusi) - lihat transactionname = "DTCXact" pada grafik deadlock. Anda harus melihat alasannya, dan ingin mengubahnya jika memungkinkan.

Tanpa eskalasi menjadi serializable, peluangnya sangat bagus bahwa kebuntuan ini tidak akan terjadi, dengan asumsi XLOCKpetunjuk dihapus. Yang mengatakan, Anda akan membaca di bawah membaca isolasi tanpa komitmen , yang datang dengan jaminan konsistensi sangat sedikit.

Jika aplikasi Anda dan kode SQL Server dapat mentolerir pembacaan versi baris, mengubah untuk membaca isolasi snapshot berkomitmen (RCSI) atau isolasi snapshot (SI) untuk bacaan juga akan menghindari kebuntuan ( XLOCKdihapus!), Sambil menghadirkan titik konsisten yang konsisten -Waktu melihat data yang berkomitmen. Ini juga mengasumsikan Anda dapat menghindari isolasi serializable, tentu saja.

Pada akhirnya, XLOCKpetunjuk ini kontra-produktif, tetapi Anda benar-benar perlu melihat alasan penggunaan tingkat isolasi serializable. Ini trancount = 2juga menarik - mungkin Anda melakukan transaksi tidak sengaja di sini. Sesuatu yang lain untuk diperiksa.

Paul White 9
sumber
2
  1. Mengurangi jumlah baris secara drastis akan mengurangi kemungkinan kebuntuan, tetapi tidak akan hilang sepenuhnya.

Secara sederhana, pilih pertama-tama menggunakan indeks untuk menentukan baris yang akan dipilih, kemudian mengambil baris, sementara sisipan memasukkan baris, kemudian mencoba memperbarui indeks (XLOCKED).

  1. Pengembang aplikasi cenderung menggunakan XLOCK jika dalam transaksi yang sama mereka ingin kemudian melakukan pembaruan pada data. Ini memastikan tidak ada yang dapat memperbarui data di bawah mereka. Saya akan menyelidiki aplikasi apa yang dilakukan untuk melihat apakah XLOCK diperlukan.

Karena itu, menghapus XLOCK mungkin tidak akan menyelesaikan masalah. SELECT akan tetap mengeluarkan kunci bersama pada indeks, dan INSERT akan menginginkan XLOCK untuk memperbaruinya. Kunci bersama dan XLOCK tidak bisa ada pada objek bersama-sama, jadi Anda masih akan mendapatkan jalan buntu. IX_Index1 harus berupa MyValue atau A, atau keduanya.

Kebuntuan jenis ini sering terjadi karena indeks yang dirancang dengan buruk dan / atau terlalu banyak indeks. Atau kode yang ditulis dengan buruk. Pilihan terbaik Anda adalah untuk melihat apakah ada beberapa cara pilih dapat ditulis ulang untuk menggunakan indeks lain.

Leo Miller
sumber