Kümelenmemiş dizinle farklı satırları güncellerken kilitlenme


13

Kimlik alanında kümelenmiş ve kümelenmemiş dizin kullandığımda kilit davranışının farklı olduğunu fark ederken bir kilitlenme sorunu çözüyorum. Kimlik alanına kümelenmiş dizin veya birincil anahtar uygulanırsa kilitlenme sorunu çözülmüş gibi görünüyor.

Farklı satırlarda bir veya daha fazla güncelleme yapan farklı işlemlerim var, örneğin A işlemi yalnızca ID = a ile satırı güncelleyecek, tx B yalnızca ID = b vb.

Ve indeks olmadan, güncellemenin tüm satırlar için güncelleme kilidi alacağını ve gerektiğinde özel kilide geçeceğini anladım, bu da sonunda kilitlenmeye yol açacak. Ama neden kümelenmemiş dizin ile, kilitlenme hala orada (isabet oranı düşmüş gibi görünüyor) bulmak için başarısız

Veri tablosu:

CREATE TABLE [dbo].[user](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [userName] [nvarchar](255) NULL,
    [name] [nvarchar](255) NULL,
    [phone] [nvarchar](255) NULL,
    [password] [nvarchar](255) NULL,
    [ip] [nvarchar](30) NULL,
    [email] [nvarchar](255) NULL,
    [pubDate] [datetime] NULL,
    [todoOrder] [text] NULL
)

Kilitlenme izi

deadlock-list
deadlock victim=process4152ca8
process-list
process id=process4152ca8 taskpriority=0 logused=0 waitresource=RID: 5:1:388:29 waittime=3308 ownerId=252354 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.947 XDES=0xb0bf180 lockMode=U schedulerid=3 kpid=11392 status=suspended spid=57 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.953 lastbatchcompleted=2014-04-11T00:15:30.950 lastattention=1900-01-01T00:00:00.950 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252354 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=62 sqlhandle=0x0200000062f45209ccf17a0e76c2389eb409d7d970b0f89e00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(2)<c/>@owner int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@owner
process id=process4153468 taskpriority=0 logused=4652 waitresource=KEY: 5:72057594042187776 (3fc56173665b) waittime=3303 ownerId=252344 transactionname=user_transaction lasttranstarted=2014-04-11T00:15:30.920 XDES=0x4184b78 lockMode=U schedulerid=3 kpid=7272 status=suspended spid=58 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2014-04-11T00:15:30.960 lastbatchcompleted=2014-04-11T00:15:30.960 lastattention=1900-01-01T00:00:00.960 clientapp=.Net SqlClient Data Provider hostname=BOOD-PC hostpid=9272 loginname=getodo_sql isolationlevel=read committed (2) xactid=252344 currentdb=5 lockTimeout=4294967295 clientoption1=671088672 clientoption2=128056
executionStack
frame procname=adhoc line=1 stmtstart=60 sqlhandle=0x02000000d4616f250747930a4cd34716b610a8113cb92fbc00000000000000000000000000000000
update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
frame procname=unknown line=1 sqlhandle=0x00000000000000000000000000000000000000000000000000000000000000000000000000000000
unknown
inputbuf
(@para0 nvarchar(61)<c/>@uid int)update [user] WITH (ROWLOCK) set [todoOrder]=@para0 where id=@uid
resource-list
ridlock fileid=1 pageid=388 dbid=5 objectname=SQL2012_707688_webows.dbo.user id=lock3f7af780 mode=X associatedObjectId=72057594042122240
owner-list
owner id=process4153468 mode=X
waiter-list
waiter id=process4152ca8 mode=U requestType=wait
keylock hobtid=72057594042187776 dbid=5 objectname=SQL2012_707688_webows.dbo.user indexname=10 id=lock3f7ad700 mode=U associatedObjectId=72057594042187776
owner-list
owner id=process4152ca8 mode=U
waiter-list
waiter id=process4153468 mode=U requestType=wait

Ayrıca ilginç ve olası bir bulgu, kümelenmiş ve kümelenmemiş dizinin farklı kilit davranışlarına sahip olduğudur.

Kümelenmiş dizini kullanırken, güncelleştirme yapılırken anahtarda özel bir kilit ve ayrıca RID'de özel bir kilit vardır; Kümelenmemiş dizin kullanılırsa iki farklı RID'de iki özel kilit bulunurken, bu beni şaşırttı.

Herkes neden bu konuda açıklayabilir, yararlı olacaktır.

Test SQL'i:

use SQL2012_707688_webows;
begin transaction;
update [user] with (rowlock) set todoOrder='{1}' where id = 63501
exec sp_lock;
commit;

Kümelenmiş Dizin olarak id ile:

spid    dbid    ObjId   IndId   Type    Resource    Mode    Status
53  5   917578307   1   KEY (b1a92fe5eed4)                      X   GRANT
53  5   917578307   1   PAG 1:879                               IX  GRANT
53  5   917578307   1   PAG 1:1928                              IX  GRANT
53  5   917578307   1   RID 1:879:7                             X   GRANT

Kümelenmemiş Dizin olarak id ile

spid    dbid    ObjId   IndId   Type    Resource    Mode    Status
53  5   917578307   0   PAG 1:879                               IX  GRANT
53  5   917578307   0   PAG 1:1928                              IX  GRANT
53  5   917578307   0   RID 1:879:7                             X   GRANT
53  5   917578307   0   RID 1:1928:18                           X   GRANT

EDIT1: herhangi bir dizin olmadan kilitlenme ayrıntıları
Diyelim ki iki tx A ve B, her biri iki güncelleme deyimleri, farklı tabii ki
tx A

update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501

tx B

update [user] with (rowlock) set todoOrder='{3}' where id = 63502
update [user] with (rowlock) set todoOrder='{4}' where id = 63502

{1} ve {4}, kilitlenme şansına sahip olacak, çünkü

{1} değerinde, tablo taraması yapması gerektiğinden satır 63502 için U kilidi isteniyor ve X kilidi, koşulla eşleştiği için satır 63501'de tutulabilirdi

{4} 'de, satır 63501 için U kilidi isteniyor ve X kilidi zaten 63502 için tutuluyor

bu yüzden txA 63501 ve 63502 beklerken txB 63502 bekleterek 63501'i bekliyor.

EDIT2: Test durumumda bir hata ortaya çıkıyor burada bir fark yaratıyor karışıklık için özür dilerim ama hata bir fark yaratır ve sonunda kilitlenmeye neden gibi görünüyor.

Paul'un analizi bu durumda bana gerçekten yardımcı olduğu için bunu bir cevap olarak kabul edeceğim.

Test durumumun hatası nedeniyle, iki işlem txA ve txB aynı satırı aşağıdaki gibi güncelleyebilir:

tx A

update [user] with (rowlock) set todoOrder='{1}' where id = 63501
update [user] with (rowlock) set todoOrder='{2}' where id = 63501

tx B

update [user] with (rowlock) set todoOrder='{3}' where id = 63501

{2} ve {3} şu durumlarda bir kilitlenme şansına sahip olacaktır:

txA, RID üzerindeki X kilidini tutarken anahtar üzerinde U kilidi ister ({1} güncellenmesi nedeniyle) txB, RID üzerindeki U kilidini tutarken, anahtar üzerindeki U kilidini tutarken


1
Bir işlemin neden aynı satırı iki kez güncellemesi gerektiğini anlayamıyorum.
ypercubeᵀᴹ

@ypercube İyi bir nokta, bu benim geliştirmem gereken bir şey. Ama bu durumda sadece kilit davranışları daha iyi anlamak istiyorum
Bood

@ypercube Daha fazla düşünceden sonra Karmaşık mantığa sahip bir uygulamanın aynı satırda iki kez aynı satırı güncellemesi gerektiğini düşünüyorum, örneğin farklı sütunlar olabilir
Bood

Yanıtlar:


16

... neden kümelenmiş dizinle, kilitlenme hala orada (isabet oranı düşmüş gibi görünse de)

Soru tam olarak net değil (örneğin id, her işlemde kaç güncelleme ve hangi değerlere ait olduğu), ancak [id]değerlerin çakıştığı ve kimliklerin olduğu tek bir işlemde birden fazla tek satırlı güncelleme ile belirgin bir kilitlenme senaryosu ortaya çıkar. farklı bir [id]sırada güncellendi :

[T1]: Update id 2; Update id 1;
[T2]: Update id 1; Update id 2;

Kilitlenme sırası: T1 (u2), T2 (u1), T1 (u1) bekleyin , T2 (u2) bekleyin .

Bu kilitlenme dizisinden, her bir işlem içindeki kimlik sırasına göre kesinlikle güncellenerek (aynı yolda aynı sırayla kilitlerin alınması) önlenebilir.

Kümelenmiş dizini kullanırken, güncelleştirme yapılırken anahtarda özel bir kilit ve ayrıca RID'de özel bir kilit vardır; Kümelenmemiş dizin kullanılırsa iki farklı RID'de iki özel kilit bulunurken, bu beni şaşırttı.

Benzersiz bir kümelenmiş dizin açıkken id, satır içi verilere yazma işlemlerini korumak için kümeleme anahtarında özel bir kilit alınır. Varsayılan olarak ayrı bir veri sayfasında saklanan RIDLOB textsütununa yazmayı korumak için ayrı bir özel kilit gerekir .

Tablo yalnızca kümelenmemiş bir dizin içeren bir yığın olduğunda id, iki şey olur. Birincisi, RIDözel bir kilit yığın satır içi verilerle ilgilidir ve diğeri LOB verisi üzerindeki kilidir. İkinci etki, daha karmaşık bir yürütme planının gerekli olmasıdır.

Kümelenmiş bir dizin ve basit bir tek değer eşitliği yüklem güncellemesiyle, sorgu işlemcisi tek bir yol kullanarak tek bir operatörde güncelleştirmeyi (okuma ve yazma) gerçekleştiren bir optimizasyon uygulayabilir:

Tek operatörlü güncelleme

Satır, yalnızca özel kilitler gerektiren tek bir arama işleminde bulunur ve güncellenir (güncelleme kilitlerine gerek yoktur). Örnek tablonuzu kullanarak örnek bir kilitleme sırası:

acquiring IX lock on OBJECT: 6:992930809:0 -- TABLE
acquiring IX lock on PAGE: 6:1:59104 -- INROW
acquiring X lock on KEY: 6:72057594233618432 (61a06abd401c) -- INROW
acquiring IX lock on PAGE: 6:1:59091 -- LOB
acquiring X lock on RID: 6:1:59091:1 -- LOB

releasing lock reference on PAGE: 6:1:59091 -- LOB
releasing lock reference on RID: 6:1:59091:1 -- LOB
releasing lock reference on KEY: 6:72057594233618432 (61a06abd401c) -- INROW
releasing lock reference on PAGE: 6:1:59104 -- INROW

Sadece kümelenmemiş bir indeksle, aynı optimizasyon uygulanamaz çünkü bir b-ağaç yapısından okumamız ve bir diğerini yazmamız gerekir. Çok yollu planın ayrı okuma ve yazma aşamaları vardır:

Çoklu yineleyici güncellemesi

Bu, okuma sırasında güncelleme kilitlerini alır ve satır uygunsa özel kilitlere dönüştürür. Şema ile örnek kilit dizisi:

acquiring IX lock on OBJECT: 6:992930809:0 -- TABLE
acquiring IU lock on PAGE: 6:1:59105 -- NC INDEX
acquiring U lock on KEY: 6:72057594233749504 (61a06abd401c) -- NC INDEX
acquiring IU lock on PAGE: 6:1:59104 -- HEAP
acquiring U lock on RID: 6:1:59104:1 -- HEAP
acquiring IX lock on PAGE: 6:1:59104 -- HEAP convert to X
acquiring X lock on RID: 6:1:59104:1 -- HEAP convert to X
acquiring IU lock on PAGE: 6:1:59091 -- LOB
acquiring U lock on RID: 6:1:59091:1 -- LOB

releasing lock reference on PAGE: 6:1:59091 
releasing lock reference on RID: 6:1:59091:1
releasing lock reference on RID: 6:1:59104:1
releasing lock reference on PAGE: 6:1:59104 
releasing lock on KEY: 6:72057594233749504 (61a06abd401c)
releasing lock on PAGE: 6:1:59105 

LOB verilerinin Tablo Güncelleme yineleyicisinde okunduğunu ve yazıldığını unutmayın . Daha karmaşık plan ve çoklu okuma ve yazma yolları, kilitlenme olasılığını artırır.

Son olarak, tablo tanımında kullanılan veri türlerini fark edemiyorum. Kullanımdan kaldırılmış textveri türünü yeni işler için kullanmamalısınız ; alternatif, gerçekten bu sütunda 2GB'a kadar veri depolama yeteneğine ihtiyacınız varsa varchar(max). Arasındaki önemli farklardan biri textve varchar(max)yani textveri dışı sıranın varsayılan olarak saklanır, süre varchar(max)içinde satır varsayılan olarak saklar.

Unicode türlerini yalnızca bu esnekliğe ihtiyacınız varsa kullanın (örneğin, bir IP adresinin neden Unicode'a ihtiyacı olacağını görmek zordur). Ayrıca, özellikleriniz için uygun uzunluk sınırlarını seçin - 255 her yerde doğru olması muhtemel görünmüyor.

Ek okuma:
Kilitlenme ve kilitlenme ortak kalıpları
Bart Duncan'ın kilitlenme sorun giderme serisi

İzleme kilitleri çeşitli şekillerde yapılabilir. Gelişmiş Hizmetlere Sahip SQL Server Express ( yalnızca 2014 ve 2012 SP1 ve üstü ), kilit edinme ve serbest bırakma ayrıntılarını görüntülemek için desteklenen bir yol olan Profiler aracını içerir .


Mükemmel cevap. "Edinme ... kilidi" ve "kilit referansını serbest bırakma" mesajlarını içeren günlükleri / izleri nasıl çıkarıyorsunuz?
Sanjiv Jivan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.