Utilizzo delle viste indicizzate per gli aggregati: troppo bello per essere vero?


28

Disponiamo di un data warehouse con un numero di record abbastanza grande (10-20 milioni di righe) e spesso eseguiamo query che contano i record tra determinate date o contano i record con determinati flag, ad es.

SELECT
    f.IsFoo,
    COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
    ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo

Le prestazioni non sono orribili, ma possono essere relativamente lente (forse 10 secondi in una cache fredda).

Recentemente ho scoperto che posso usare GROUP BYnelle viste indicizzate e quindi ho provato qualcosa di simile al seguente

CREATE VIEW TestView
WITH SCHEMABINDING
AS
    SELECT
        Date,
        FlagId,
        COUNT_BIG(*) AS WidgetCount
    FROM Widgets
    GROUP BY Date, FlagId;
GO

CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
    Date,
    FlagId
);

Di conseguenza, le prestazioni della mia prima query sono ora <100 ms e la vista e l'indice risultanti sono <100 k (sebbene il nostro conteggio delle righe sia ampio, l'intervallo di date e ID flag indica che questa vista contiene solo 1000-2000 righe).

Ho pensato che forse questo avrebbe compromesso le prestazioni delle scritture nella tabella Widget, ma no - le prestazioni di inserimenti e aggiornamenti in questa tabella sono praticamente intatte per quanto potrei dire (in più, essendo un data warehouse questa tabella viene aggiornata di rado Comunque)

A me sembra troppo bello per essere vero - vero? Cosa devo fare attenzione quando uso le viste indicizzate in questo modo?


2
Puoi riscrivere i tuoi script in modo che siano effettivamente validi SQL? Il tuo SELECTe gli CREATE VIEWscript sono sbagliati, come credo sia il tuo CREATE INDEXscript.
Mark Sinkinson,

2
@MarkSinkinson Apologies, risulta che provare a scrivere un SQL valido per tabelle immaginarie è difficile
Giustino

La parte "troppo bella per essere vera" per me è arrivata quando volevo viste più avanzate, come quelle che contenevano MAX, self o join esterni, o indicizzare una vista che a sua volta fa riferimento a un'altra vista, che almeno in SQL Server non lo sono consentito docs.microsoft.com/en-us/sql/relational-database/views/… . Quindi finisco sempre per diventare troppo ambizioso e poi dover ridimensionare le cose. Ma per le aggregazioni più semplici sono davvero eccezionali - anche SUM è supportato.
Simon_Weaver

Risposte:


29

Come hai notato, la vista stessa materializza solo un piccolo numero di righe, quindi anche se aggiorni l'intera tabella, l' I / O aggiuntivo coinvolto nell'aggiornamento della vista è trascurabile. Probabilmente hai già sentito il dolore più grande che sentirai quando hai creato la vista. Il prossimo più vicino sarà se aggiungi un gazillion righe alla tabella di base con un gruppo di nuovi ID che richiedono nuove righe nella vista.

Questo non è troppo bello per essere vero. Stai utilizzando le visualizzazioni indicizzate esattamente come dovevano essere utilizzate - o almeno uno dei modi più efficaci: pagare per aggregazioni di query future al momento della scrittura. Funziona meglio quando il risultato è molto più piccolo della fonte e ovviamente quando le aggregazioni sono richieste più spesso rispetto all'aggiornamento dei dati sottostanti (più comune in DW che OLTP, in generale).

Sfortunatamente molte persone pensano che indicizzare una vista sia magico: un indice non renderà tutte le viste più efficienti, specialmente quelle che uniscono semplicemente le tabelle e / o producono lo stesso numero di righe dell'origine (o addirittura si moltiplicano). In questi casi l'I / O dalla vista è uguale o addirittura peggiore della query originale, non solo perché ci sono le stesse o più righe, ma spesso memorizzano e materializzano anche più colonne. Quindi materializzare quelli in anticipo non fornisce alcun vantaggio, poiché - anche con gli SSD - I / O, la rete e l'elaborazione / rendering dei client rimangono ancora i principali colli di bottiglia nel restituire grandi set di risultati al client. I risparmi ottenuti evitando il join in fase di esecuzione non sono misurabili rispetto a tutte le altre risorse che stai ancora utilizzando.

Come gli indici non cluster, fai solo attenzione a non esagerare. Se aggiungi 10 diverse viste indicizzate a una tabella, vedrai un maggiore impatto sulla porzione di scrittura del tuo carico di lavoro, soprattutto se le colonne di raggruppamento non sono (nella) chiave di clustering.

Accidenti, intendevo blog su questo argomento.


19

Le risposte di Aarons hanno coperto bene questa domanda. Due cose da aggiungere:

  1. Le visualizzazioni indicizzate di aggregazione possono causare conflitti e deadlock su più righe. Normalmente, due inserti non si bloccano (ad eccezione di condizioni piuttosto rare come l'escalation dei blocchi o le collisioni di hash dei blocchi). Ma se entrambi gli inserti si rivolgono allo stesso gruppo nella vista, contenderanno. Lo stesso punto sta per qualsiasi altra cosa che accetta i blocchi (DML, suggerimenti sui blocchi).
  2. Anche le viste indicizzate che non aggregano possono essere utili. Ti consentono di indicizzare su colonne da più tabelle. In questo modo è possibile filtrare in modo efficiente su una tabella e ordinare per colonna da una tabella unita. Tale modello può convertire l'unione a tabella intera in piccole query a tempo costante.

Ho usato entrambe le visualizzazioni di aggregazione e di join con estremo vantaggio.

Tutto sommato, il tuo caso d'uso sembra un caso perfetto. Le viste indicizzate sono una tecnica molto sottoutilizzata.

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.