Birleştirme deyimi kilitlenme kendisi


22

Aşağıdaki yordam var (SQL Server 2008 R2):

create procedure usp_SaveCompanyUserData
    @companyId bigint,
    @userId bigint,
    @dataTable tt_CoUserdata readonly
as
begin

    set nocount, xact_abort on;

    merge CompanyUser with (holdlock) as r
    using (
        select 
            @companyId as CompanyId, 
            @userId as UserId, 
            MyKey, 
            MyValue
        from @dataTable) as newData
    on r.CompanyId = newData.CompanyId
        and r.UserId = newData.UserId
        and r.MyKey = newData.MyKey
    when not matched then
        insert (CompanyId, UserId, MyKey, MyValue) values
        (@companyId, @userId, newData.MyKey, newData.MyValue);

end;

Şirket Kimliği, Kullanıcı Kimliği, MyKey, hedef tablo için bileşik anahtar oluşturur. Şirket Kimliği, bir ana tablonun yabancı bir anahtarıdır. Ayrıca, üzerinde kümelenmemiş bir dizin var CompanyId asc, UserId asc.

Birçok farklı konudan çağrılır ve sürekli olarak aynı ifadeyi çağıran farklı süreçler arasında kilitlenmeler alıyorum. Anladığım kadarıyla yarış koşulu hatalarını eklemek / güncellemek için "with (holdlock)" gerekliydi.

İki farklı iş parçacığının satırları (ya da sayfaları) sınırları doğrularken ve aynı zamanda kilitlenmeye başladıklarında, farklı sıralarda kilitlediklerini varsayıyorum.

bu doğru bir varsayım mı?

Bu durumu çözmenin en iyi yolu nedir (örneğin; kilitlenme yok, çok iş parçacıklı performans üzerinde minimum etki)?

Sorgu Planı Görüntüsü (Resmi yeni bir sekmede görüntülerseniz, okunabilir. Küçük boyut için özür dilerim.)

  • Veri tablosunda en fazla 28 satır vardır.
  • Kodda geriye döndüm ve burada bir işlem başlatacağımız yeri göremiyorum.
  • Yabancı anahtar yalnızca silme işleminde basamaklı olarak ayarlanmıştır ve ana tablodan silinme olmamıştır.

Yanıtlar:


12

Tamam, birkaç kez her şeye baktıktan sonra, temel varsayımınızın doğru olduğunu düşünüyorum. Muhtemelen burada olan şey şudur:

  1. MERGE'in MATCH kısmı dizini eşleşmeler için kontrol eder ve bu satırları / sayfaları olduğu gibi okuma-kilitlenir.

  2. Eşleşmeyen bir satırı olduğunda, önce yeni Dizin Satırını eklemeye çalışır, böylece bir satır / sayfa yazma kilidi ister ...

Ancak, aynı satırda / sayfada başka bir kullanıcı da 1. adıma geçerse, ilk kullanıcı Güncellemeden engellenir ve ...

İkinci kullanıcının da aynı sayfaya eklemesi gerekiyorsa, kilitlenme içerisindedirler.

AFAIK, bu prosedürle çıkmaza giremediğinizden% 100 emin olmanın ve MERGE'a TABLOCKX ipucu eklemenin tek bir basit yolu var, ancak bunun performans üzerinde gerçekten kötü bir etkisi olacak.

Öyle mümkün yerine TABLOCK ipucu ekleyerek Big ile performansınıza bir etkiye sahip olmaksızın sorunu çözmek için yeterli olacağını.

Son olarak, PAGLOCK, XLOCK veya hem PAGLOCK hem de XLOCK eklemeyi deneyebilirsiniz. Yine bu işe yarayabilir ve performans çok kötü olmayabilir . Görmek için denemek zorunda kalacaksın.


Anlık görüntü yalıtım seviyesinin (satır sürümleme) burada yardımcı olabileceğini düşünüyor musunuz?
Mikael Eriksson,

Olabilir. Veya kilitlenme özel durumlarını eşzamanlılık istisnalarına dönüştürebilir.
RBarryYoung,

2
TABLOCK ipucunu bir INSERT ifadesinin hedefi olan bir tabloda belirtmek, TABLOCKX ipucunu belirtmekle aynı etkiye sahiptir. (Kaynak: msdn.microsoft.com/en-us/library/bb510625.aspx )
tuespetre

31

Tablo değişkeni yalnızca bir değer elde ederse sorun olmazdı. Birden çok satırda, kilitlenme için yeni bir olasılık var. Aynı şirket için (1, 2) ve (2, 1) içeren tablo değişkenleriyle çalışan iki eşzamanlı işlemin (A ve B) olduğunu varsayalım.

İşlem A, hedefi okur, satır bulamaz ve '1' değerini ekler. '1' değerinde özel bir satır kilidi tutar. B işlemi hedefi okur, hiçbir satır bulamaz ve '2' değerini ekler. '2' değerinde özel bir satır kilidi tutar.

Şimdi A işleminin 2. satırı işlemesi ve B işleminin 1. satırı işlemesi gerekir. İki işlem de diğer işlem tarafından tutulan özel kilitle uyumlu olmayan bir kilit gerektirdiğinden hiçbir işlem ilerleyemez.

Birden çok satırlı kilitlenmeleri önlemek için, satırların her seferinde aynı sırada işlenmesi (ve erişilen tablolara) gerekir . Soruda gösterilen yürütme planındaki tablo değişkeni bir öbektür, bu nedenle satırların kendine özgü bir sırası yoktur (bu garantili olmasa da ekleme sırasına göre okunma olasılığı oldukça yüksektir):

Mevcut plan

Tutarlı sıra işleme sırasının olmaması doğrudan kilitlenme fırsatına yol açar. İkinci bir husus, anahtarlıkta benzersiz bir güvencenin bulunmamasının, doğru Cadılar Bayramı Koruması sağlamak için bir Tablo Makarası gerektiği anlamına gelmesidir. Makara, istekli bir makaradır, yani tüm satırlar, okuma işleminden önce okunup tekrar oynatılmadan önce bir tempdb çalışma tablasına yazılır .

TYPETablo değişkeninin kümelenmesini içerecek şekilde yeniden tanımlanması PRIMARY KEY:

DROP TYPE dbo.CoUserData;

CREATE TYPE dbo.CoUserData
AS TABLE
(
    MyKey   integer NOT NULL PRIMARY KEY CLUSTERED,
    MyValue integer NOT NULL
);

Yürütme planı şimdi kümelenmiş dizinin taranmasını gösteriyor ve benzersizlik garantisi, optimize edicinin Tablo Makarasını güvenle kaldırabildiği anlamına geliyor:

Birincil anahtarla

MERGE128 ipliğin ifadesinde 5000 yineleme bulunan testlerde , kümelenmiş tablo değişkeni ile kilitlenme meydana gelmedi. Bunun sadece gözlem temelinde olduğunu vurgulamalıyım; kümelenmiş tablo değişkeni ayrıca ( teknik olarak ) sıralarını çeşitli sıralarda üretebilir, ancak tutarlı bir sıra şansı çok büyük ölçüde artar. Tabii ki, her yeni toplu güncelleme, servis paketi veya yeni SQL Server sürümü için gözlenen davranışın tekrar test edilmesi gerekecektir.

Tablo değişken tanımının değiştirilememesi durumunda, başka bir alternatif daha vardır:

MERGE dbo.CompanyUser AS R
USING 
    (SELECT DISTINCT MyKey, MyValue FROM @DataTable) AS NewData ON
    R.CompanyId = @CompanyID
    AND R.UserID = @UserID
    AND R.MyKey = NewData.MyKey
WHEN NOT MATCHED THEN 
    INSERT 
        (CompanyID, UserID, MyKey, MyValue) 
    VALUES
        (@CompanyID, @UserID, NewData.MyKey, NewData.MyValue)
OPTION (ORDER GROUP);

Bu aynı zamanda, açık bir sıralama getirme pahasına makarayı (ve sıra sıra tutarlılığı) ortadan kaldırmayı da başarır:

Planı sırala

Bu plan aynı testi kullanarak hiçbir kilitlenme üretmedi. Aşağıdaki üreme komut dosyası:

CREATE TYPE dbo.CoUserData
AS TABLE
(
    MyKey   integer NOT NULL /* PRIMARY KEY */,
    MyValue integer NOT NULL
);
GO
CREATE TABLE dbo.Company
(
    CompanyID   integer NOT NULL

    CONSTRAINT PK_Company
        PRIMARY KEY (CompanyID)
);
GO
CREATE TABLE dbo.CompanyUser
(
    CompanyID   integer NOT NULL,
    UserID      integer NOT NULL,
    MyKey       integer NOT NULL,
    MyValue     integer NOT NULL

    CONSTRAINT PK_CompanyUser
        PRIMARY KEY CLUSTERED
            (CompanyID, UserID, MyKey),

    FOREIGN KEY (CompanyID)
        REFERENCES dbo.Company (CompanyID),
);
GO
CREATE NONCLUSTERED INDEX nc1
ON dbo.CompanyUser (CompanyID, UserID);
GO
INSERT dbo.Company (CompanyID) VALUES (1);
GO
DECLARE 
    @DataTable AS dbo.CoUserData,
    @CompanyID integer = 1,
    @UserID integer = 1;

INSERT @DataTable
SELECT TOP (10)
    V.MyKey,
    V.MyValue
FROM
(
    VALUES
        (1, 1),
        (2, 2),
        (3, 3),
        (4, 4),
        (5, 5),
        (6, 6),
        (7, 7),
        (8, 8),
        (9, 9)
) AS V (MyKey, MyValue)
ORDER BY NEWID();

BEGIN TRANSACTION;

    -- Test MERGE statement here

ROLLBACK TRANSACTION;

8

SQL_Kiwi'nin çok iyi bir analiz sağladığını düşünüyorum. Veritabanındaki sorunu çözmeniz gerekirse, önerisini izlemelisiniz. Elbette, her yükseltme işleminizde, bir hizmet paketi uyguladığınızda veya bir dizin veya dizine eklenmiş bir görünüm eklediğinizde / değiştirdiğinizde hala sizin için işe yaradığını yeniden test etmeniz gerekir.

Başka üç alternatif var:

  1. Eklentilerinizi çarpışmayacak şekilde serileştirebilirsiniz: işleminizin başında sp_getapplock işlevini çağırabilir ve MERGE cihazınızı çalıştırmadan önce özel bir kilit elde edebilirsiniz. Tabii ki hala stres testi yapman gerekiyor.

  2. Tüm uçlarınızı tek bir iş parçacığına sahip olabilirsiniz, böylece uygulama sunucunuz eşzamanlılığı yönetir.

  3. Kilitlenmelerden sonra otomatik olarak yeniden deneyebilirsiniz - eşzamanlılık yüksekse bu en yavaş yaklaşım olabilir.

Her iki durumda da, çözümünüzün performans üzerindeki etkisini belirleyebilirsiniz.

Genelde sistemimizde kilitlenme yoktur, ancak bunlara sahip olmak için çok fazla potansiyelimiz vardır. 2011'de bir dağıtımda hata yaptık ve hepsi aynı senaryoyu izleyen birkaç saat içinde yarım düzine kilitlenme meydana geldi. Yakında düzelttim ve bu yıl için tüm kilitlenmeler oldu.

Sistemimizde çoğunlukla yaklaşım 1'i kullanıyoruz. Bu bizim için gerçekten iyi çalışıyor.


-1

Başka bir olası yaklaşım - Bazen kilitleme ve performans sorunlarını sunmak için Birleştirme buldum - Seçenek (MaxDop x) sorgu seçeneğiyle oynamaya değer olabilir

Loş ve uzak geçmişte SQL Server, bir Satır Düzeyi Kilitleme seçeneğine sahipti - ancak bu bir ölümden ölmüş gibi görünüyor, ancak kimliği olan bir küme PK, eklerin temiz çalışmasını sağlamalıdır.

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.