Di salah satu database kami, kami memiliki tabel yang secara intensif diakses secara bersamaan oleh banyak utas. Utas memperbarui atau menyisipkan baris MERGE. Ada juga utas yang kadang-kadang menghapus baris, sehingga data tabel sangat fluktuatif. Utas yang melakukan upert terkadang mengalami kebuntuan. Masalahnya terlihat mirip dengan yang dijelaskan dalam pertanyaan ini . Perbedaannya adalah, dalam kasus kami, masing-masing utas melakukan pembaruan atau memasukkan tepat satu baris .
Setup yang disederhanakan mengikuti. Tabel ini tumpukan dengan dua indeks nonclustered unik berakhir
CREATE TABLE [Cache]
(
[UID] uniqueidentifier NOT NULL CONSTRAINT DF_Cache_UID DEFAULT (newid()),
[ItemKey] varchar(200) NOT NULL,
[FileName] nvarchar(255) NOT NULL,
[Expires] datetime2(2) NOT NULL,
CONSTRAINT [PK_Cache] PRIMARY KEY NONCLUSTERED ([UID])
)
GO
CREATE UNIQUE INDEX IX_Cache ON [Cache] ([ItemKey]);
GO
dan permintaan tipikal adalah
DECLARE
@itemKey varchar(200) = 'Item_0F3C43A6A6A14255B2EA977EA730EDF2',
@fileName nvarchar(255) = 'File_0F3C43A6A6A14255B2EA977EA730EDF2.dat';
MERGE INTO [Cache] WITH (HOLDLOCK) T
USING (
VALUES (@itemKey, @fileName, dateadd(minute, 10, sysdatetime()))
) S(ItemKey, FileName, Expires)
ON T.ItemKey = S.ItemKey
WHEN MATCHED THEN
UPDATE
SET
T.FileName = S.FileName,
T.Expires = S.Expires
WHEN NOT MATCHED THEN
INSERT (ItemKey, FileName, Expires)
VALUES (S.ItemKey, S.FileName, S.Expires)
OUTPUT deleted.FileName;
yaitu pencocokan terjadi dengan kunci indeks unik. Petunjuk HOLDLOCKada di sini, karena konkurensi (seperti yang disarankan di sini ).
Saya melakukan penyelidikan kecil dan berikut ini adalah apa yang saya temukan.
Dalam sebagian besar kasus, rencana eksekusi kueri adalah
dengan pola penguncian berikut
yaitu IXkunci pada objek diikuti oleh kunci lebih rinci.
Namun, kadang-kadang, rencana eksekusi permintaan berbeda
(bentuk rencana ini dapat dipaksa dengan menambahkan INDEX(0)petunjuk) dan pola pengunciannya
Xkunci pemberitahuan ditempatkan pada objek setelah IXditempatkan.
Karena dua IXkompatibel, tetapi dua Xtidak, hal yang terjadi di bawah konkurensi adalah
jalan buntu !
Dan di sini bagian pertama dari pertanyaan muncul. Apakah menempatkan Xkunci pada objek setelah IXmemenuhi syarat? Bukankah itu bug?
Kunci inten dinamai kunci maksud karena mereka diperoleh sebelum kunci pada level yang lebih rendah, dan oleh karena itu sinyal maksud untuk menempatkan kunci pada level yang lebih rendah .
dan juga
IX berarti niat untuk memperbarui hanya beberapa baris daripada semuanya
jadi, menempatkan Xkunci pada objek setelah IXterlihat SANGAT mencurigakan bagi saya.
Pertama saya mencoba untuk mencegah kebuntuan dengan mencoba menambahkan petunjuk penguncian tabel
MERGE INTO [Cache] WITH (HOLDLOCK, TABLOCK) T
dan
MERGE INTO [Cache] WITH (HOLDLOCK, TABLOCKX) T
dengan TABLOCKpola penguncian di tempat menjadi
dan dengan TABLOCKXpola penguncian adalah
karena dua SIX(dan juga dua X) tidak kompatibel ini mencegah kebuntuan secara efektif, tetapi, sayangnya, mencegah konkurensi juga (yang tidak diinginkan).
Upaya saya berikutnya adalah menambah PAGLOCKdan ROWLOCKmembuat kunci lebih granular dan mengurangi pertikaian. Keduanya tidak memiliki efek ( Xpada objek masih diamati segera setelah IX).
Upaya terakhir saya adalah memaksa bentuk rencana eksekusi "baik" dengan penguncian granular yang baik dengan menambahkan FORCESEEKpetunjuk
MERGE INTO [Cache] WITH (HOLDLOCK, FORCESEEK(IX_Cache(ItemKey))) T
dan itu berhasil.
Dan di sini muncul pertanyaan kedua . Mungkinkah itu FORCESEEKakan diabaikan dan pola penguncian yang buruk akan digunakan? (Seperti yang saya sebutkan, PAGLOCKdan ROWLOCKtampaknya diabaikan).
Menambahkan UPDLOCKtidak berpengaruh ( Xpada objek yang masih dapat diamati setelah IX).
Membuat IX_Cacheindeks berkerumun, seperti yang diantisipasi, berhasil. Itu mengarah ke rencana dengan Clustered Index Seek dan penguncian granular. Selain itu saya mencoba memaksa Scan Indeks Clustered yang ditampilkan penguncian granular juga.
Namun. Pengamatan tambahan. Di pengaturan awal bahkan dengan FORCESEEK(IX_Cache(ItemKey)))di tempat, jika seseorang mengubah @itemKeydeklarasi variabel dari varchar (200) ke nvarchar (200) , rencana eksekusi menjadi
melihat bahwa mencari digunakan, TETAPI pola penguncian dalam kasus ini lagi menunjukkan Xkunci ditempatkan pada objek setelah IX.
Jadi, tampaknya mencari paksa tidak selalu menjamin kunci granular (dan deadlock tidak ada karenanya). Saya tidak yakin, bahwa memiliki pengelompokan indeks jaminan granular. Atau apakah itu?
Pemahaman saya (koreksi saya jika saya salah) adalah bahwa penguncian adalah situasional dalam tingkat yang besar, dan bentuk rencana eksekusi tertentu tidak menyiratkan pola penguncian tertentu.
Pertanyaan tentang kelayakan menempatkan Xkunci pada objek setelah IXmasih terbuka. Dan jika memenuhi syarat, adakah yang bisa dilakukan seseorang untuk mencegah penguncian objek?










