Quali fattori vanno nell'indice cluster di una vista indicizzata selezionato?


19

In breve
Quali sono i fattori che influenzano la selezione di Optimizer dall'indice di una vista indicizzata?

Per me, le viste indicizzate sembrano sfidare ciò che ho capito su come lo Strumento per ottimizzare sceglie gli indici. L'ho già visto prima , ma l'OP non è stato accolto molto bene. Sto davvero cercando guide , ma inventerò uno pseudo esempio, quindi pubblicherò un esempio reale con un sacco di DDL, output, esempi.

Supponiamo che sto usando Enterprise 2008+, capire with(noexpand)

Esempio di pseudo

Prendi questo pseudo esempio: creo una vista con 22 join, 17 filtri e un pony circus che attraversa un gruppo di 10 milioni di tabelle di righe. Questa visione è costosa (sì, con la E maiuscola) da materializzare. SCHEMABIND e indicizzerò la vista. Quindi a SELECT a,b FROM AnIndexedView WHERE theClusterKeyField < 84. Nella logica di Optimizer che mi sfugge vengono eseguiti i join sottostanti.

Il risultato:

  • Nessun suggerimento: 4825 letture per 720 righe, 47 cpu su 76ms e un costo sotto l'albero stimato di 0,30523.
  • Con suggerimento: 17 letture, 720 righe, 15 cpu su 4ms e un costo sottotree stimato di 0,007253

Quindi cosa sta succedendo qui? L'ho provato in Enterprise 2008, 2008-R2 e 2012. Secondo ogni metrica posso pensare di utilizzare l'indice della vista in modo molto più efficiente. Non ho problemi di sniffing dei parametri o dati distorti, dato che si tratta di un trampolino pubblicitario.

Un esempio reale (lungo)

A meno che tu non sia un tocco masochista, probabilmente non hai bisogno o non vuoi leggere questa parte.

La versione
Sì, impresa.

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 10 feb 2012 19:39:15 Copyright (c) Microsoft Corporation Enterprise Edition (64 bit) su Windows NT 6.2 (build 9200:) (Hypervisor)

La vista

CREATE VIEW dbo.TimelineMaterialized    WITH SCHEMABINDING
AS
SELECT  TM.TimelineID,
        TM.TimelineTypeID,
        TM.EmployeeID,
        TM.CreateUTC,
        CUL.CultureCode,
        CASE 
           WHEN TM.CustomerMessageID    > 0 THEN TM.CustomerMessageID
           WHEN TM.CustomerSessionID    > 0 THEN TM.CustomerSessionID
           WHEN TM.NewItemTagID         > 0 THEN TM.NewItemTagID
           WHEN TM.OutfitID             > 0 THEN TM.OutfitID
           WHEN TM.ProductTransactionID > 0 THEN TM.ProductTransactionID
           ELSE 0 END  As HrefId,
        CASE 
          WHEN TM.CustomerMessageID    > 0 THEN IsNull(C.Name, 'N/A')   
          WHEN TM.CustomerSessionID    > 0 THEN IsNull(C.Name, 'N/A')
          WHEN TM.NewItemTagID         > 0 THEN IsNull(NI.Title, 'N/A')
          WHEN TM.OutfitID             > 0 THEN IsNull(O.Name, 'N/A')
          WHEN TM.ProductTransactionID > 0 THEN IsNull(PT_PL.NameLocalized, 'N/A')
                 END as HrefText

FROM       dbo.Timeline TM
INNER JOIN dbo.CustomerSession    CS    ON TM.CustomerSessionID    = CS.CustomerSessionID
INNER JOIN dbo.CustomerMessage    CM    ON TM.CustomerMessageID    = CM.CustomerMessageID
INNER JOIN dbo.Outfit             O     ON PO.OutfitID             = O.OutfitID
INNER JOIN dbo.ProductTransaction PT    ON TM.ProductTransactionID = PT.ProductTransactionID
INNER JOIN dbo.Product            PT_P  ON PT.ProductID            = PT_P.ProductID
INNER JOIN dbo.ProductLang        PT_PL ON PT_P.ProductID          = PT_PL.ProductID
INNER JOIN dbo.Culture            CUL   ON PT_PL.CultureID         = CUL.CultureID
INNER JOIN dbo.NewsItemTag        NIT   ON TM.NewsItemTagID        = NIT.NewsItemTagID
INNER JOIN dbo.NewsItem           NI    ON NIT.NewsItemID          = NI.NewsItemID
INNER JOIN dbo.Customer           C     ON  C.CustomerID = CASE 
                                             WHEN TM.TimelineTypeID = 1 THEN CM.CustomerID 
                                             WHEN TM.TimelineTypeID = 5 THEN CS.CustomerID
                                             ELSE 0 END

WHERE        CUL.IsActive = 1

Indice cluster

CREATE UNIQUE CLUSTERED INDEX PK_TimelineMaterialized  ON 
                   TimelineMaterialized (EmployeeID, CreateUTC, CultureCode, TimelineID)

Test SQL

-- NO HINT - - -  - - -  - - -  - - -  - - - 
SELECT  *                 --yes yes, star is bad ...just a test example
FROM    TimelineMaterialized TM 
WHERE 
            TM.EmployeeID   = 2
        AND TM.CultureCode  = 'en-US'
        AND TM.CreateUTC    > '9/10/2012'
        AND TM.CreateUTC    < '9/11/2012'

-- WITH HINT - - -  - - -  - - -  - - -  - - - 
SELECT  *               
FROM    TimelineMaterialized TM with(noexpand)
WHERE 
            TM.EmployeeID   = 2
        AND TM.CultureCode  = 'en-US'
        AND TM.CreateUTC    > '9/10/2012'
        AND TM.CreateUTC    < '9/11/2012'

Risultato = 11 righe di output

11 righe di output - lo stesso per entrambe le query

Output del profiler
Le prime 4 righe sono prive di suggerimenti. Le 4 righe inferiori utilizzano il suggerimento.

Profiler

Piani di esecuzione
GitHub Gist per entrambi i piani di esecuzione in formato SQLPlan

Nessun piano di esecuzione dei suggerimenti: perché non utilizzare l'indice cluster che le ho dato Mr. SQL? È clusterd sui 3 campi filtro. Provalo, potrebbe piacerti.
Nessun suggerimento: enorme piano di esecuzione

Piano semplice quando si utilizza un suggerimento.

Utilizzo del suggerimento: piano di esecuzione semplice


Risposte:


26

La corrispondenza delle viste indicizzate è un'operazione relativamente costosa *, quindi l'ottimizzatore prova prima altre trasformazioni facili e veloci. Se quelli producono un piano economico (0,05 unità nel tuo caso) l'ottimizzazione termina presto. La scommessa è che l'ottimizzazione continua richiederebbe più tempo di quanto risparmiato. Ricorda che l'obiettivo principale dell'ottimizzatore è rapidamente un piano "abbastanza buono".

L'uso dell'indice cluster nella vista non è costoso in sé, ma può essere il processo di abbinamento di un albero di query logico a potenziali viste indicizzate. Come ho detto in un commento sull'altra domanda, il riferimento alla vista nella query viene espanso prima dell'ottimizzazione, quindi l'ottimizzatore non sa che hai scritto la query sulla vista in primo luogo - vede solo l'albero espanso (come se la vista era stata allineata).

"Buon piano sufficiente" significa che l'ottimizzatore ha trovato un piano decente e si è fermato all'inizio di una fase di esplorazione. "TimeOut" significa che ha superato il numero di passaggi di ottimizzazione che si è impostato come "budget" all'inizio della fase corrente.

Il budget è impostato in base al costo del miglior piano trovato in una fase precedente. Con una query così a basso costo (0,05) il numero di mosse preventivate sarà piuttosto piccolo e rapidamente esaurito dalla trasformazione regolare dato il numero di join coinvolti nella query di esempio (ci sono molti modi per riorganizzare i join interni, ad esempio) .

Se sei interessato a saperne di più sul perché la corrispondenza della vista indicizzata è costosa e quindi lasciata a fasi successive di ottimizzazione e / o considerata solo per query più costose, ci sono due Microsoft Research Papers sull'argomento qui (pdf) e qui (citeseer ).

Un altro fattore rilevante è che la corrispondenza della vista indicizzata non è disponibile nella fase di ottimizzazione 0 (elaborazione della transazione).

Ulteriori letture:

Visualizzazioni e statistiche indicizzate

* e disponibile solo in Enterprise Edition (o equivalente)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.