Kilitlenmelerin ana nedenleri nelerdir ve bunlar önlenebilir mi?


55

Son zamanlarda ASP.NET uygulamalarımızdan biri bir veritabanı kilitlenme hatası gösterdi ve hatayı kontrol edip düzeltmem istendi. Kilitlenmenin nedenini bir imleç içindeki tabloyu titizlikle güncelleyen saklı bir prosedür olduğunu bulmayı başardım.

Bu hatayı ilk defa görüyorum ve etkili bir şekilde nasıl izleyeceğinizi ve düzelteceğimi bilmiyordum. Bildiğim tüm yolları denedim ve nihayet güncellenmekte olan tablonun birincil anahtarının olmadığını buldum! Neyse ki bir kimlik sütunu idi.

Daha sonra dağıtım için veritabanı komut dosyası geliştirici dağılmış buldum. Birincil anahtar ekledim ve sorun çözüldü.

Mutlu hissettim ve projeme geri döndüm ve bu çıkmazın nedenini bulmak için biraz araştırma yaptım ...

Görünüşe göre, kilitlenmeye neden olan dairesel bir bekleme durumuydu. Güncelleştirmeler, bir birincil anahtar olmadan, birincil anahtardan daha uzun sürüyor.

Bunun iyi tanımlanmış bir sonuç olmadığını biliyorum, bu yüzden burada gönderiyorum ...

  • Eksik ana anahtar sorun mu?
  • Karşılıklı çıkmaza neden olan başka koşullar var mı (karşılıklı dışlanma, beklet ve bekle, ön hazırlık ve döngüsel beklemeden)?
  • Kilitlenmeleri nasıl önler ve izlerim?

2
Gördüğüm çıkma kilitlerin IME çoğunluğu (hepsi?), Dairesel bekleyişler nedeniyle (özellikle aşırı tetikleyicilerin aşırı kullanımı nedeniyle) oluyor.
Sathyajith Bhat

Dairesellık, kilitlenmenin gerekli koşullarından biridir. Tüm oturumlarınız aynı sırada kilit alırsa, herhangi bir kilitlenmeden kaçınabilirsiniz.
Peter G.

Yanıtlar:


38

kilitlenmeleri izlemek ikisinin kolay yolu:

Varsayılan olarak, kilitlenmeler hata günlüğüne yazılmaz. SQL'in 1204 ve 3605 izleme bayraklarıyla hata günlüğüne kilitlenmeler yazmasına neden olabilirsiniz.

Kilitlenme bilgilerini SQL Server hata günlüğüne yazın: DBCC TRACEON (-1, 1204, 3605)

Kapatın: DBCC TRACEOFF (-1, 1204, 3605)

İzleme bayrağı 1204 ve çalıştırıldığında alacağınız çıktı hakkında bir tartışma için "Sorun Giderme Kilitlenmeleri" bölümüne bakın. https://msdn.microsoft.com/en-us/library/ms178104.aspx

Önleme daha zordur, esas olarak aşağıdakilere dikkat etmeniz gerekir:

Kod Bloğu 1, önce A kaynağını sonra da B kaynağını bu sırada kilitler.

Kod Bloğu 2, B kaynağını sonra da A kaynağını bu sırada kilitler.

Bu, her iki kaynağın kilitlenmesi atom değilse, Kod Bloğu 1 A'yı kilitleyebilir ve önlenebilir, sonra Kod Bloğu 2 A'nın işlem zamanını almadan önce B'yi kilitler. Şimdi kilitlenme var.

Bu durumu önlemek için aşağıdaki gibi bir şey yapabilirsiniz

Kod Bloğu A (psuedo kodu)

Lock Shared Resource Z
    Lock Resource A
    Lock Resource B
Unlock Shared Resource Z
...

Kod Bloğu B (sözde kod)

Lock Shared Resource Z
    Lock Resource B
    Lock Resource A
Unlock Shared Resource Z
...

A ve B'yi açtıklarında kilidini açmayı unutma

bu, A kod bloğu ve B kod bloğu arasında kilitlenmeyi önler

Bir veritabanı açısından bakıldığında, kilitler veritabanının kendisi tarafından işlendiğinden, yani verileri güncellerken satır / tablo kilitlendiğinden, bu durumu nasıl önleyeceğimizden emin değilim. Gördüğüm en çok görülen sorun, bir imlecin içinde seninkini gördüğün yer. İmleçler kötü bir şekilde verimsizdir, mümkünse bunlardan kaçının.


A kodunu B kodundan B kodundan önce kilitlemek mi istediniz? Yazıldığı gibi, bu, kilitlenmelere neden olur .. daha önce yaptığınız yorumlarda sizin belirttiğiniz gibi. Bu kilitleme sırasını sağlamak için başlangıçta boş sorulara ihtiyaç duysanız bile, kaynakları her zaman aynı sırayla kilitlemek istiyorsunuz.
Gerard ONeill

23

kilitlenmeler hakkında okumak ve öğrenmek için en sevdiğim makaleler: Basit Konuşma - Kilitlenmeleri takip etme ve SQL Server Central - Kilitlenmeleri gidermek için Profiler kullanma . Size bir durum emmek nasıl ele alınacağına dair örnekler ve tavsiyeler vereceklerdir.

Kısacası, mevcut bir sorunu çözmek için ilgili işlemleri daha kısa hale getirir, gereksiz kısmını çıkarır, nesnelerin kullanım sırasına bakar, gerçekte hangi yalıtım seviyesine ihtiyaç duyulduğunu görürüm, gereksiz okumadım veri...

Ancak makaleleri daha iyi okuyun, önerileri çok daha iyi olacaktır.


16

Bazen bir kilitlenme, veritabanının tüm tablodan ziyade bireysel kayıtları kilitlemesine izin verdiği için indeksleme ekleyerek çözülebilir, böylece çekişmeyi ve işlerin sıkışma olasılığını azaltırsınız.

Örneğin, InnoDB’de :

İfadenize uygun hiçbir indeksiniz yoksa ve MySQL'in ifadeyi işlemek için tüm tabloyu taraması gerekiyorsa, tablonun her satırı kilitlenir ve bu da diğer kullanıcıların tabloya girdiği tüm ekleri engeller. Sorgularınızın gereksiz yere birçok satırı taramaması için iyi dizinler oluşturmak önemlidir.

Başka bir yaygın çözüm, gerekmediğinde işlem tutarlılığını kapatmak ya da izolasyon seviyenizi değiştirmek , örneğin istatistikleri hesaplamak için uzun süredir devam eden bir işi değiştirmek ... yakın bir cevap genellikle yeterlidir, kesin sayılara ihtiyacınız yoktur, altından değişiyorlar gibi. Ve tamamlanması 30 dakika sürerse, bu tablolardaki diğer tüm işlemleri durdurmasını istemezsiniz.

...

Onları izlemeye gelince, kullandığınız veritabanı yazılımına bağlıdır.


Oyları düşürürken yorum yapmanız olağan bir nezaket ... Bu geçerli bir cevap, bir masa kilidine yükseltme ve sonsuza dek alma seçkin bir ifade kesinlikle bir kilitlenmeye neden olabilir.
BlackICE

1
MS SQLServer, dizinler kümelenmemişse beklenmeyen bir kilitleme davranışı da verebilir. Satır düzeyinde kilitleme kullanmak yönünüzü sessizce dikkate almaz ve sayfa düzeyinde kilitleme yapar. Daha sonra sayfada bekleyen kilitlenmeleri alabilirsiniz.
Jay

7

Sadece imleç olayını geliştirmek için. gerçekten çok kötü. Tüm masayı kilitler, sonra sıraları teker teker işler.

Bir süre döngüsü kullanarak imleç biçimindeki satırlarda dolaşmak en iyisidir

While döngüsünde, döngüdeki her satır için bir seçim yapılır ve kilit, o sırada yalnızca bir satırda gerçekleşir. Tablodaki verilerin geri kalanı sorgulama için ücretsizdir, bu sayede kilitlenme olma ihtimalini azaltır.

Ayrıca daha hızlı. Yine de neden imleçler olduğunu merak ettin.

İşte bu tür bir yapı örneği:

DECLARE @LastID INT = (SELECT MAX(ID) FROM Tbl)
DECLARE @ID     INT = (SELECT MIN(ID) FROM Tbl)
WHILE @ID <= @LastID
    BEGIN
    IF EXISTS (SELECT * FROM Tbl WHERE ID = @ID)
        BEGIN
        -- Do something to this row of the table
        END

    SET @ID += 1  -- Don't forget this part!
    END

Kimlik alanınız seyrekse, ayrı bir kimlik listesi almak isteyebilir ve aşağıdakileri yineleyebilirsiniz:

DECLARE @IDs TABLE
    (
    Seq INT NOT NULL IDENTITY PRIMARY KEY,
    ID  INT NOT NULL
    )
INSERT INTO @IDs (ID)
    SELECT ID
    FROM Tbl
    WHERE 1=1  -- Criteria here

DECLARE @Rec     INT = 1
DECLARE @NumRecs INT = (SELECT MAX(Seq) FROM @IDs)
DECLARE @ID      INT
WHILE @Rec <= @NumRecs
    BEGIN
    SET @ID = (SELECT ID FROM @IDs WHERE Seq = @Seq)

    -- Do something to this row of the table

    SET @Seq += 1  -- Don't forget this part!
    END

6

Birincil anahtarın eksik olmaması sorun değil . En azından kendi başına. İlk olarak, indekslere sahip olmak için bir birincil ihtiyacınız yok. İkincisi, tablo taramaları yapıyor olsanız bile (bu, özel sorgunuz bir dizin kullanmıyorsa gerçekleşmesi gereken bir durumdur), bir tablo kilidi kendiliğinden kilitlenmeye neden olmaz. Yazma işlemi bir okuma için bekler ve okuma işlemi yazmak için bekleyin ve elbette birbirlerini beklemek zorunda kalmayacağınızı okudum.

Diğer cevaplara ek olarak, işlem yalıtım seviyesi önemlidir, çünkü tekrarlanabilir okuma ve serileştirme, 'okuma' kilitlerinin işlemin sonuna kadar tutulmasına neden olan şeydir. Bir kaynağın kilitlenmesi kilitlenmeye neden olmaz. Kilitli tutmak yapar. Yazma işlemleri her zaman kaynaklarını işlemin sonuna kadar kilitli tutar.

Favori kilit önleme stratejim, 'anlık görüntü' özelliklerini kullanıyor. Taahhüt Edilen Anlık Görüntü özelliği, okumaların kilit kullanmama anlamına gelir! Ayrıca 'Okuma kararlı' öğesinden daha fazla kontrole ihtiyacınız olursa, 'Hızlı atış izolasyon seviyesi' özelliği vardır. Bu işlem diğer oyuncuları bloke etmemekle birlikte seri hale getirilmiş (burada MS terimlerini kullanarak) bir işlem yapılmasını sağlar.

Son olarak, bir güncelleme kilidi kullanılarak bir sınıf kilitlenme önlenebilir. Okumayı basılı tutarsanız (HOLD veya Tekrarlanabilir Okuyucuyu kullanarak) ve başka bir işlem de aynı şeyi yaparsa, her ikisi de aynı kayıtları güncellemeye çalışırsa, kilitlenme yaşarsınız. Ancak her ikisi de bir güncelleme kilidi isterse, ikinci işlem birinciyi bekleyecek, diğer işlemlerde ise veriler gerçekten yazılana kadar paylaşılan kilitleri kullanarak verileri okumaya izin verecektir. Bu, işlemlerden biri hala paylaşılan bir HOLD kilidi talep ederse işe yaramaz.


-2

İmleçler SQL Server'da yavaş olsa da, imlecin kaynak verilerini Temp tablosuna çekip imleci üzerinde çalıştırarak imlecin kilitlenmesini önleyebilirsiniz. Bu, imlecin gerçek veri tablosunu kilitlemesini önler ve elde ettiğiniz tek kilit, imlecin içinde değil, yalnızca ekleme / güncelleme süresince tutulan ve imlecin içinde gerçekleştirilen güncellemeler veya ekler içindir.

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.