özet
Ana sorunlar:
- Optimize edicinin plan seçimi, değerlerin düzgün bir dağılımını varsayar.
- Uygun indeks eksikliği:
- Tabloyu taramak tek seçenektir.
- Bir birleşim naif iç içe döngüler bir yerine, katılmak endeksi iç içe döngüler katılmak. Saf bir birleşmede, birleşim tahminleri birleşimin iç tarafına itilmek yerine birleşimde değerlendirilir.
ayrıntılar
İki plan temel olarak oldukça benzer, ancak performans çok farklı olabilir:
Ek sütunlarla planlama
Önce makul bir süre içinde tamamlanmayan fazladan sütunları olanı almak:
İlginç özellikler:
- 0 düğümündeki Üst 100'e döndürülen satırları sınırlar. Ayrıca, optimize edici için bir satır hedefi belirler, bu nedenle planın altındaki her şey ilk 100 satırı hızlı bir şekilde döndürmek için seçilir.
- Düğüm 4'te tara,
Start_Time
boş olmayan State
, 3 veya 4 Operation_Type
olan ve listelenen değerlerden biri olan tablodaki satırları bulur . Tablo bir kez tamamen taranır, her satır belirtilen tahminlere göre test edilir. Yalnızca tüm testleri geçen satırlar Sıralama'ya akar. Optimize edici, 38.283 satırın hak kazanacağını tahmin ediyor.
- Düğüm 3'teki sırala, düğüm 4'teki tara'daki tüm satırları tüketir ve sıralarına göre sıralar
Start_Time DESC
. Bu, sorgu tarafından istenen son sunum sırasıdır.
- Optimize edici, tüm planın 100 satır döndürmesi için 93 satırın (aslında 93.2791) Sırala'dan okunması gerektiğini tahmin eder (birleştirmenin beklenen etkisini hesaba katar).
- Düğüm 2'de birleştirilen İç İçe Döngülerin iç girdisini (alt dal) 94 kez (aslında 94.2791) yürütmesi beklenir. Ek satır, teknik nedenlerden dolayı düğüm 1'deki durdurma paralellik değişimiyle gereklidir.
- Düğüm 5'teki Tara, her yinelemede tabloyu tam olarak tarar.
Start_Time
Boş olmayan ve State
3 veya 4 olan satırları bulur . Bu, her yinelemede 400.875 satır ürettiği tahmin edilmektedir. 94.2791 yinelemenin üzerinde toplam satır sayısı neredeyse 38 milyon.
- Düğüm 2'deki İç İçe Döngüler birleşimi de birleşim tahminlerini uygular. O denetler
Operation_Type
olduğunu, maçların Start_Time
düğümden 4 den az olduğu Start_Time
yani düğüm 5'ten Start_Time
düğümden 5 den az olan Finish_Time
düğüm 4'ten ve iki o Id
değerleri eşleşmiyor.
- Düğüm 1'deki Akışları Topla (paralellik değişimini durdur), 100 sıra üretilinceye kadar her bir iplikten sıralı akışları birleştirir. Birden fazla akışta birleştirmenin düzenini koruyan doğası, 5. adımda bahsedilen fazladan satırı gerektiren şeydir.
Büyük verimsizlik açıkça yukarıdaki 6. ve 7. adımlardadır. Her yineleme için 5 numaralı düğümde tabloyu tam olarak taramak, optimize edicinin tahmin ettiği gibi yalnızca 94 kez gerçekleşirse bile biraz makul olabilir. Düğüm 2'deki sıra başına ~ 38 milyon karşılaştırma seti de büyük bir maliyettir.
En önemlisi, değerlerin dağılımına bağlı olduğu için 93/94 sıralı satır hedef tahmininin de yanlış olması muhtemeldir. Optimize edici, daha ayrıntılı bilgi olmadan tekdüze bir dağıtım olduğunu varsayar. Basit bir deyişle, tablodaki satırların% 1'inin nitelendirilmesi bekleniyorsa, optimize edici 1 eşleşen satır bulmanın nedenlerini belirtirse, 100 satırı okuması gerekir.
Bu sorguyu tamamlanmaya kadar çalıştırdıysanız (ki bu çok uzun zaman alabilir), büyük olasılıkla sonunda 100 satır üretmek için 93/94 satırdan daha fazlasının Sıradan okunması gerektiğini görürsünüz. En kötü durumda, 100. sıra Sıradaki son satır kullanılarak bulunacaktır. Optimize edicinin düğüm 4'teki tahmininin doğru olduğu varsayılırsa, bu, Taramayı düğüm 15'te 38.284 kez, toplamda 15 milyar sıra gibi çalıştırmak demektir . Tarama tahminleri de kapalıysa daha fazla olabilir.
Bu yürütme planı ayrıca eksik bir dizin uyarısı içerir:
/*
The Query Processor estimates that implementing the following index
could improve the query cost by 72.7096%.
WARNING: This is only an estimate, and the Query Processor is making
this recommendation based solely upon analysis of this specific query.
It has not considered the resulting index size, or its workload-wide
impact, including its impact on INSERT, UPDATE, DELETE performance.
These factors should be taken into account before creating this index.
*/
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[Batch_Tasks_Queue] ([Operation_Type],[State],[Start_Time])
INCLUDE ([Id],[Parameters])
Optimize edici, tabloya bir dizin eklemenin performansı artıracağı konusunda sizi uyarıyor.
Fazladan sütunlar olmadan planlama
Bu esasen öncekiyle aynı plan olup, düğüm 6'ya İndeks Biriktirme ve düğüm 5'e Filtre ekleme ile önemli farklar şunlardır:
- Düğüm 6'daki Dizin Biriktirme bir Eager Biriktir. Bu hevesle altındaki taramasının sonuçlarını tüketir ve üzerine kademeli geçici endeksi oluşturur
Operation_Type
ve Start_Time
birlikte Id
olmayan bir anahtar sütun olarak.
- Düğüm 2'deki İç İçe Döngüler Birleştirme artık bir dizin birleşimidir. Resim birleştirme sayıda dayanak buradan yerine başına yineleme akım değerleri değerlendirilir
Operation_Type
, Start_Time
, Finish_Time
ve Id
düğüm 4 de tarama dış referans olarak iç yan dal geçirilir.
- Düğüm 7'de tarama yalnızca bir kez gerçekleştirilir.
- Düğüm 6 Endeksi biriktirme geçici dizinden satır arar
Operation_Type
mevcut dış referans değeri ile eşleşen ve Start_Time
tanımlanan aralığındadır Start_Time
ve Finish_Time
dış referanslar.
- Düğüm 5'teki Filtre
Id
, Dizin Biriktiricisi'ndeki değerleri, geçerli dış referans değerine karşı eşitsizlik açısından test eder Id
.
Önemli geliştirmeler:
- İç taraf taraması sadece bir kez yapılır
- Dahil edilen sütun olarak (
Operation_Type
, Start_Time
) üzerindeki geçici bir dizin, Id
bir dizin iç içe döngülerin birleşmesine izin verir. Dizin, her seferinde tüm tabloyu taramak yerine her bir yinelemede eşleşen satırları aramak için kullanılır.
Daha önce olduğu gibi, optimize edici eksik bir dizin hakkında bir uyarı içerir:
/*
The Query Processor estimates that implementing the following index
could improve the query cost by 24.1475%.
WARNING: This is only an estimate, and the Query Processor is making
this recommendation based solely upon analysis of this specific query.
It has not considered the resulting index size, or its workload-wide
impact, including its impact on INSERT, UPDATE, DELETE performance.
These factors should be taken into account before creating this index.
*/
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[Batch_Tasks_Queue] ([State],[Start_Time])
INCLUDE ([Id],[Operation_Type])
GO
Sonuç
Optimize edici sizin için geçici bir dizin oluşturmayı seçtiğinden fazladan sütun içermeyen plan daha hızlıdır.
Fazladan sütunlar içeren plan, geçici dizinin oluşturulmasını daha pahalı hale getirir. [Parameters
] Sütun nvarchar(2000)
indeksi her satır için 4000 bayt kadar ekleyin hangi. Ek maliyet, optimize ediciyi her bir yürütme için geçici dizin oluşturmanın kendisi için ödeme yapmayacağına ikna etmek için yeterlidir.
Doktor her iki durumda da kalıcı bir endeksin daha iyi bir çözüm olacağı konusunda uyarıyor. Endeksin ideal bileşimi daha geniş iş yükünüze bağlıdır. Bu özel sorgu için önerilen dizinler makul bir başlangıç noktasıdır, ancak ilgili avantajları ve maliyetleri anlamanız gerekir.
Öneri
Bu sorgu için çok çeşitli olası indeksler faydalı olacaktır. Önemli paket, bir çeşit kümelenmemiş endeksin gerekli olmasıdır. Sağlanan bilgilerden, bence makul bir dizin:
CREATE NONCLUSTERED INDEX i1
ON dbo.Batch_Tasks_Queue (Start_Time DESC)
INCLUDE (Operation_Type, [State], Finish_Time);
Ben de sorguyu biraz daha iyi organize etmek ve [Parameters]
en iyi 100 satır ( Id
anahtar olarak kullanarak ) bulunana kadar kümelenmiş dizinde geniş sütunları ararken gecikme cazip olurdu :
SELECT TOP (100)
BTQ1.id,
BTQ2.id,
BTQ3.[Parameters],
BTQ4.[Parameters]
FROM dbo.Batch_Tasks_Queue AS BTQ1
JOIN dbo.Batch_Tasks_Queue AS BTQ2 WITH (FORCESEEK)
ON BTQ2.Operation_Type = BTQ1.Operation_Type
AND BTQ2.Start_Time > BTQ1.Start_Time
AND BTQ2.Start_Time < BTQ1.Finish_Time
AND BTQ2.id != BTQ1.id
-- Look up the [Parameters] values
JOIN dbo.Batch_Tasks_Queue AS BTQ3
ON BTQ3.Id = BTQ1.Id
JOIN dbo.Batch_Tasks_Queue AS BTQ4
ON BTQ4.Id = BTQ2.Id
WHERE
BTQ1.[State] IN (3, 4)
AND BTQ2.[State] IN (3, 4)
AND BTQ1.Operation_Type NOT IN (23, 24, 25, 26, 27, 28, 30)
AND BTQ2.Operation_Type NOT IN (23, 24, 25, 26, 27, 28, 30)
-- These predicates are not strictly needed
AND BTQ1.Start_Time IS NOT NULL
AND BTQ2.Start_Time IS NOT NULL
ORDER BY
BTQ1.Start_Time DESC;
Nerede [Parameters]
sütunlar gerekli değildir, sorgu basitleştiirlebilir:
SELECT TOP (100)
BTQ1.id,
BTQ2.id
FROM dbo.Batch_Tasks_Queue AS BTQ1
JOIN dbo.Batch_Tasks_Queue AS BTQ2 WITH (FORCESEEK)
ON BTQ2.Operation_Type = BTQ1.Operation_Type
AND BTQ2.Start_Time > BTQ1.Start_Time
AND BTQ2.Start_Time < BTQ1.Finish_Time
AND BTQ2.id != BTQ1.id
WHERE
BTQ1.[State] IN (3, 4)
AND BTQ2.[State] IN (3, 4)
AND BTQ1.Operation_Type NOT IN (23, 24, 25, 26, 27, 28, 30)
AND BTQ2.Operation_Type NOT IN (23, 24, 25, 26, 27, 28, 30)
AND BTQ1.Start_Time IS NOT NULL
AND BTQ2.Start_Time IS NOT NULL
ORDER BY
BTQ1.Start_Time DESC;
FORCESEEK
İpucu iyileştirici bir endeksli iç içe döngüler planı seçer sağlamak yardımına yoktur (kuyu bu tür çalışmalara değil eğilimindedir aksi bir karma ya da (çok-çok) birleştirme katılmak seçmek için optimize edici için maliyet esaslı günaha vardır Her ikisi de büyük artıklarla sonuçlanır; karma durumunda kova başına birçok öğe ve birleştirme için birçok geri sarma).
Alternatif
Sorgu (belirli değerleri dahil) özellikle okuma performansı için kritik olsaydı, bunun yerine iki filtrelenmiş dizin düşünürdüm:
CREATE NONCLUSTERED INDEX i1
ON dbo.Batch_Tasks_Queue (Start_Time DESC)
INCLUDE (Operation_Type, [State], Finish_Time)
WHERE
Start_Time IS NOT NULL
AND [State] IN (3, 4)
AND Operation_Type <> 23
AND Operation_Type <> 24
AND Operation_Type <> 25
AND Operation_Type <> 26
AND Operation_Type <> 27
AND Operation_Type <> 28
AND Operation_Type <> 30;
CREATE NONCLUSTERED INDEX i2
ON dbo.Batch_Tasks_Queue (Operation_Type, [State], Start_Time)
WHERE
Start_Time IS NOT NULL
AND [State] IN (3, 4)
AND Operation_Type <> 23
AND Operation_Type <> 24
AND Operation_Type <> 25
AND Operation_Type <> 26
AND Operation_Type <> 27
AND Operation_Type <> 28
AND Operation_Type <> 30;
[Parameters]
Sütuna ihtiyaç duymayan sorgu için, filtrelenmiş dizinleri kullanan tahmini plan:
Dizin taraması, ek tahminleri değerlendirmeden tüm uygun satırları otomatik olarak döndürür. İç içe geçmiş döngülerin birleştiği her yineleme için, dizin araması iki arama işlemi gerçekleştirir:
- Üzerinde bir arama öneki eşleşmesi
Operation_Type
ve State
= 3, daha sonra Start_Time
değerlerin aralığını , Id
eşitsizlik üzerine artık yüklemeyi aramak .
- Üzerinde bir arama öneki eşleşmesi
Operation_Type
ve State
= 4, daha sonra Start_Time
değerlerin aralığını , Id
eşitsizlik üzerine artık yüklemeyi aramak .
[Parameters]
Sütunun gerekli olduğu yerlerde, sorgu planı her tablo için en fazla 100 tekil arama ekler:
Son bir not olarak, numeric
uygulanabilir olduğu yerde yerleşik standart tamsayı türlerini kullanmayı düşünmelisiniz .