Tarama neden bu yüklem için arama yapmaktan daha hızlı?


30

Beklenmeyen olarak tanımlayacağım bir sorgu performansı sorunu oluşturmayı başardım. İnternete odaklanmış bir cevap arıyorum.

Makinemde, aşağıdaki sorgu kümelenmiş bir dizin taraması yapar ve yaklaşık 6.8 saniye CPU süresi alır:

SELECT ID1, ID2
FROM two_col_key_test WITH (FORCESCAN)
WHERE ID1 NOT IN
(
N'1', N'2',N'3', N'4', N'5',
N'6', N'7', N'8', N'9', N'10',
N'11', N'12',N'13', N'14', N'15',
N'16', N'17', N'18', N'19', N'20'
)
AND (ID1 = N'FILLER TEXT' AND ID2 >= N'' OR (ID1 > N'FILLER TEXT'))
ORDER BY ID1, ID2 OFFSET 12000000 ROWS FETCH FIRST 1 ROW ONLY
OPTION (MAXDOP 1);

Aşağıdaki sorgu, kümelenmiş bir dizin araması yapar (yalnızca fark FORCESCANipucunu kaldırmaktır ) ancak yaklaşık 18.2 saniye CPU süresi alır:

SELECT ID1, ID2
FROM two_col_key_test
WHERE ID1 NOT IN
(
N'1', N'2',N'3', N'4', N'5',
N'6', N'7', N'8', N'9', N'10',
N'11', N'12',N'13', N'14', N'15',
N'16', N'17', N'18', N'19', N'20'
)
AND (ID1 = N'FILLER TEXT' AND ID2 >= N'' OR (ID1 > N'FILLER TEXT'))
ORDER BY ID1, ID2 OFFSET 12000000 ROWS FETCH FIRST 1 ROW ONLY
OPTION (MAXDOP 1);

Sorgu planları oldukça benzer. Her iki sorgu için kümelenmiş dizinden okunan 120000001 satır var:

sorgu planları

SQL Server 2017 CU 10'dayım. İşte two_col_key_testtablo oluşturmak ve doldurmak için kod :

drop table if exists dbo.two_col_key_test;

CREATE TABLE dbo.two_col_key_test (
    ID1 NVARCHAR(50) NOT NULL,
    ID2 NVARCHAR(50) NOT NULL,
    FILLER NVARCHAR(50),
    PRIMARY KEY (ID1, ID2)
);

DROP TABLE IF EXISTS #t;

SELECT TOP (4000) 0 ID INTO #t
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);


INSERT INTO dbo.two_col_key_test WITH (TABLOCK)
SELECT N'FILLER TEXT' + CASE WHEN ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) > 8000000 THEN N' 2' ELSE N'' END
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
, NULL
FROM #t t1
CROSS JOIN #t t2;

Çağrı yığını raporlamasından daha fazlasını yapan bir cevap bekliyorum. Örneğin sqlmin!TCValSSInRowExprFilter<231,0,0>::GetDataX, yavaş sorguda hızlı olana kıyasla önemli ölçüde daha fazla CPU döngüsü olduğunu görebiliyorum :

perview

Orada durmak yerine, bunun ne olduğunu ve neden iki sorgu arasında bu kadar büyük bir fark olduğunu anlamak istiyorum.

Bu iki sorgu için neden CPU süresinde büyük bir fark var?

Yanıtlar:


31

Bu iki sorgu için neden CPU süresinde büyük bir fark var?

Tarama planı, her satır için aşağıdaki itilmiş sarsılmaz (artık) yüklemeyi değerlendirir:

[two_col_key_test].[ID1]<>N'1' 
AND [two_col_key_test].[ID1]<>N'10' 
AND [two_col_key_test].[ID1]<>N'11' 
AND [two_col_key_test].[ID1]<>N'12' 
AND [two_col_key_test].[ID1]<>N'13' 
AND [two_col_key_test].[ID1]<>N'14' 
AND [two_col_key_test].[ID1]<>N'15' 
AND [two_col_key_test].[ID1]<>N'16' 
AND [two_col_key_test].[ID1]<>N'17' 
AND [two_col_key_test].[ID1]<>N'18' 
AND [two_col_key_test].[ID1]<>N'19' 
AND [two_col_key_test].[ID1]<>N'2' 
AND [two_col_key_test].[ID1]<>N'20' 
AND [two_col_key_test].[ID1]<>N'3' 
AND [two_col_key_test].[ID1]<>N'4' 
AND [two_col_key_test].[ID1]<>N'5' 
AND [two_col_key_test].[ID1]<>N'6' 
AND [two_col_key_test].[ID1]<>N'7' 
AND [two_col_key_test].[ID1]<>N'8' 
AND [two_col_key_test].[ID1]<>N'9' 
AND 
(
    [two_col_key_test].[ID1]=N'FILLER TEXT' 
    AND [two_col_key_test].[ID2]>=N'' 
    OR [two_col_key_test].[ID1]>N'FILLER TEXT'
)

artık tarama

Arama planı iki arama işlemi yapar:

Seek Keys[1]: 
    Prefix: 
    [two_col_key_test].ID1 = Scalar Operator(N'FILLER TEXT'), 
        Start: [two_col_key_test].ID2 >= Scalar Operator(N'')
Seek Keys[1]: 
    Start: [two_col_key_test].ID1 > Scalar Operator(N'FILLER TEXT')

... tahminin bu kısmına uyması için:

(ID1 = N'FILLER TEXT' AND ID2 >= N'' OR (ID1 > N'FILLER TEXT'))

Bir artık yüklem, yukarıdaki arama koşullarını geçen satırlara uygulanır (örneğinizdeki tüm satırlar).

Bununla birlikte, her bir eşitsizlik için iki ayrı testler ile değiştirildiği daha az OR daha büyüktür :

([two_col_key_test].[ID1]<N'1' OR [two_col_key_test].[ID1]>N'1') 
AND ([two_col_key_test].[ID1]<N'10' OR [two_col_key_test].[ID1]>N'10') 
AND ([two_col_key_test].[ID1]<N'11' OR [two_col_key_test].[ID1]>N'11') 
AND ([two_col_key_test].[ID1]<N'12' OR [two_col_key_test].[ID1]>N'12') 
AND ([two_col_key_test].[ID1]<N'13' OR [two_col_key_test].[ID1]>N'13') 
AND ([two_col_key_test].[ID1]<N'14' OR [two_col_key_test].[ID1]>N'14') 
AND ([two_col_key_test].[ID1]<N'15' OR [two_col_key_test].[ID1]>N'15') 
AND ([two_col_key_test].[ID1]<N'16' OR [two_col_key_test].[ID1]>N'16') 
AND ([two_col_key_test].[ID1]<N'17' OR [two_col_key_test].[ID1]>N'17') 
AND ([two_col_key_test].[ID1]<N'18' OR [two_col_key_test].[ID1]>N'18') 
AND ([two_col_key_test].[ID1]<N'19' OR [two_col_key_test].[ID1]>N'19') 
AND ([two_col_key_test].[ID1]<N'2' OR [two_col_key_test].[ID1]>N'2') 
AND ([two_col_key_test].[ID1]<N'20' OR [two_col_key_test].[ID1]>N'20') 
AND ([two_col_key_test].[ID1]<N'3' OR [two_col_key_test].[ID1]>N'3') 
AND ([two_col_key_test].[ID1]<N'4' OR [two_col_key_test].[ID1]>N'4') 
AND ([two_col_key_test].[ID1]<N'5' OR [two_col_key_test].[ID1]>N'5') 
AND ([two_col_key_test].[ID1]<N'6' OR [two_col_key_test].[ID1]>N'6') 
AND ([two_col_key_test].[ID1]<N'7' OR [two_col_key_test].[ID1]>N'7') 
AND ([two_col_key_test].[ID1]<N'8' OR [two_col_key_test].[ID1]>N'8') 
AND ([two_col_key_test].[ID1]<N'9' OR [two_col_key_test].[ID1]>N'9')

artık aramak

Her eşitsizliğin yeniden yazılması örneğin:

[ID1] <> N'1'  ->  [ID1]<N'1' OR [ID1]>N'1'

... burada üretkenlik var. Harmanlamaya duyarlı dize karşılaştırmaları pahalıdır. Karşılaştırma sayısının iki katına çıkarılması, gördüğünüz CPU zamanındaki farkın çoğunu açıklar.

Bunu, daha belirgin bir şekilde belgesiz iz bayrağı 9130 ile sarsılmaz tahminde bulunmayı engelleyerek devre dışı bırakarak görebilirsiniz. Bu, artıkları ayrı olarak inceleyebileceğiniz performans bilgileriyle ayrı bir Filtre olarak gösterecektir:

taramak

aramak

Bu aynı zamanda, arama üzerinde yanlış olan önemsizliği vurgulayacaktır, bu da optimizer'ın neden taramadan önce araştırmayı seçtiğini açıklar (arayan kısmın bazı satırları ortadan kaldırmayı beklemesi bekleniyor).

Eşitsizliğin yeniden yazılması (muhtemelen filtrelenmiş) endeks eşleşmesini mümkün kılarken (b-ağacı indekslerinin aranma yeteneğini en iyi şekilde kullanmak için), her iki yarının da artık kalması durumunda, bu genişlemeyi geri almak daha iyi olacaktır. Bunu SQL Server geribildirim sitesinde bir gelişme olarak önerebilirsiniz .

Ayrıca, orijinal ("eski") kardinalite kestirimi modelinin, bu sorgu için varsayılan olarak bir tarama seçtiğini unutmayın.

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.