Temp masası neden Cadılar Bayramı Sorunu'na istekli bir makaradan daha verimli bir çözümdür?


14

Bir kaynak tablodan yalnızca hedef tabloda yoksa satır ekleyen aşağıdaki sorguyu göz önünde bulundurun:

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);

Olası bir plan şekli bir birleştirme birleşimi ve istekli bir makara içerir. İstekli biriktirme operatörü Cadılar Bayramı Sorununu çözmek için hazır :

ilk plan

Makinemde yukarıdaki kod yaklaşık 6900 ms'de çalışıyor. Tabloları oluşturmak için repro kodu sorunun altında yer almaktadır. Performanstan memnun kalmazsam, istekli makaraya güvenmek yerine geçici bir tabloya eklenecek satırları yüklemeyi deneyebilirim. İşte olası bir uygulama:

DROP TABLE IF EXISTS #CONSULTANT_RECOMMENDED_TEMP_TABLE;
CREATE TABLE #CONSULTANT_RECOMMENDED_TEMP_TABLE (
    ID BIGINT,
    PRIMARY KEY (ID)
);

INSERT INTO #CONSULTANT_RECOMMENDED_TEMP_TABLE WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1);

Yeni kod yaklaşık 4400 ms'de yürütülür. Gerçek planları alabilir ve operatör düzeyinde zamanın nerede harcandığını incelemek için Gerçek Zaman İstatistikleri ™ 'ni kullanabilirim. Gerçek bir plan istemenin, toplamlar önceki sonuçlarla eşleşmemesi için bu sorgular için önemli bir ek yük getirdiğini unutmayın.

╔═════════════╦═════════════╦══════════════╗
  operator    first query  second query 
╠═════════════╬═════════════╬══════════════╣
 big scan     1771         1744         
 little scan  163          166          
 sort         531          530          
 merge join   709          669          
 spool        3202         N/A          
 temp insert  N/A          422          
 temp scan    N/A          187          
 insert       3122         1545         
╚═════════════╩═════════════╩══════════════╝

İstekli makaralı sorgu planı, geçici tablo kullanan plana kıyasla kesici uç ve makara operatörleri üzerinde önemli ölçüde daha fazla zaman harcıyor gibi görünüyor.

Geçici tabloya sahip plan neden daha verimli? Istekli bir makara zaten sadece bir iç sıcaklık tablo değil mi? İçerilere odaklanan cevaplar aradığımı düşünüyorum. Çağrı yığınlarının nasıl farklı olduğunu görebiliyorum ancak büyük resmi anlayamıyorum.

Birisi bilmek istediği takdirde SQL Server 2017 CU 11 kullanıyorum. Yukarıdaki sorgularda kullanılan tabloları doldurmak için kod:

DROP TABLE IF EXISTS dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR;

CREATE TABLE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR (
ID BIGINT NOT NULL,
PRIMARY KEY (ID)
);

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (20000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
CROSS JOIN master..spt_values t3
OPTION (MAXDOP 1);


DROP TABLE IF EXISTS dbo.A_HEAP_OF_MOSTLY_NEW_ROWS;

CREATE TABLE dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (
ID BIGINT NOT NULL
);

INSERT INTO dbo.A_HEAP_OF_MOSTLY_NEW_ROWS WITH (TABLOCK)
SELECT TOP (1900000) 19999999 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Yanıtlar:


14

Ben buna Cadılar Bayramı Koruması diyorum .

Güncelleme Sorgularını Optimize Etme makalemde bir güncelleme ifadesiyle kullanılmasının bir örneğini bulabilirsiniz . Örneğin, senaryonuzla ilgili ise, ayrı sorgular yürütülürken hedef tabloyu tüm eşzamanlı değişikliklere karşı kilitleyerek aynı anlambilimi korumak için biraz dikkatli olunmalıdır.

Geçici tabloya sahip plan neden daha verimli? Istekli bir makara zaten sadece bir iç sıcaklık tablo değil mi?

Bir makara geçici tablonun bazı özelliklerine sahiptir, ancak ikisi tam eşdeğer değildir. Özellikle, bir makara esasen bir b-ağacı yapısına satır satır sırasız eklentidir . Kilitleme ve günlüğe kaydetme optimizasyonlarından yararlanır, ancak toplu yük optimizasyonlarını desteklemez .

Sonuç olarak, genellikle sorguyu doğal bir şekilde bölerek daha iyi performans elde edilebilir: Yeni satırları geçici bir tabloya veya değişkene toplu yükleme, ardından geçici nesneden optimize edilmiş bir ekleme (açık Cadılar Bayramı Koruması olmadan) gerçekleştirme.

Bu ayrımı yapmak, orijinal ifadenin okuma ve yazma kısımlarını ayrı ayrı ayarlamanız için ekstra özgürlük sağlar.

Bir yan not olarak, Cadılar Bayramı Sorununun satır sürümleri kullanılarak nasıl ele alınabileceğini düşünmek ilginçtir. Belki de SQL Server'ın gelecekteki bir sürümü bu özelliği uygun koşullarda sağlayacaktır.


Michael Kutz'un bir yorumda belirttiği gibi, açık HP'yi önlemek için delik doldurma optimizasyonunu kullanma olasılığını da keşfedebilirsiniz . Demo için bunu başarmanın bir yolu, IDsütununda benzersiz bir dizin (isterseniz kümelenmiş) oluşturmaktır A_HEAP_OF_MOSTLY_NEW_ROWS.

CREATE UNIQUE INDEX i ON dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (ID);

Bu garanti uygulandığında, optimize edici delik doldurma ve satır kümesi paylaşımını kullanabilir:

MERGE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (SERIALIZABLE) AS HICETY
USING dbo.A_HEAP_OF_MOSTLY_NEW_ROWS AS AHOMNR
    ON AHOMNR.ID = HICETY.ID
WHEN NOT MATCHED BY TARGET
THEN INSERT (ID) VALUES (AHOMNR.ID);

MERGE planı

İlginç olsa da, dikkatli bir şekilde uygulanan Manuel Cadılar Bayramı Koruması'nı uygulayarak birçok durumda daha iyi performans elde edebileceksiniz.


5

Paul'ün cevabını biraz genişletmek için, makara ve geçici tablo yaklaşımları arasındaki geçen zaman farkının bir kısmı DML Request Sort, makara planındaki seçenek için destek eksikliğine iniyor gibi görünüyor . Belgelenmemiş izleme bayrağı 8795 ile, geçici tablo yaklaşımı için geçen süre 4400 ms'den 5600 ms'ye atlar.

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1, QUERYTRACEON 8795);

Bunun, biriktirme planı tarafından gerçekleştirilen eke tam olarak eşdeğer olmadığını unutmayın. Bu sorgu, işlem günlüğüne çok daha fazla veri yazar.

Aynı etki bazı hile ile ters olarak görülebilir. SQL Server'ı Cadılar Bayramı Koruması için bir makara yerine bir tür kullanmaya teşvik etmek mümkündür. Bir uygulama:

INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (987654321) 
maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
    WHERE maybe_new_rows.ID = halloween.ID
)
ORDER BY maybe_new_rows.ID, maybe_new_rows.ID + 1
OPTION (MAXDOP 1, QUERYTRACEON 7470, MERGE JOIN);

Şimdi plan, makara yerine bir TOP N Sort operatörüne sahip. Sıralama bir engelleme işlecidir, bu nedenle makara artık gerekli değildir:

resim açıklamasını buraya girin

Daha da önemlisi, artık bu DML Request Sortseçeneği destekliyoruz . Gerçek Zaman İstatistiklerine tekrar bakıldığında, insert operatörü şimdi sadece 1623 ms alır. Tüm planın gerçek bir plan talep etmeden yürütülmesi yaklaşık 5400 ms sürer.

Hugo'nun açıkladığı gibi, Eager Biriktiricisi operatörü düzeni korur. Bu en kolay bir TOP PERCENTplanla görülebilir . Makara ile yapılan orijinal sorgunun makaradaki verilerin sıralı doğasından daha iyi yararlanamaması talihsiz bir durumdur.

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.