Impaginazione in SQL Server


17

Ho un database molto grande, circa 100 GB. Sto eseguendo la query:

select * from <table_name>;

e voglio mostrare solo le righe dalla 100 alla 200.

Voglio capire come ciò avvenga internamente. Il database recupera tutti i record dal disco in memoria e restituisce dal client alla query dalla 100 ° alla 400 ° riga? Oppure esiste un meccanismo, in modo che solo quei record (100-200) siano recuperati dal database, usando meccanismi di indicizzazione come alberi B, ecc.?

Ho scoperto che questo è legato al concetto di impaginazione, ma non sono riuscito a trovare esattamente come accade internamente a livello di database.

Risposte:


37

Nella query che hai pubblicato:

select * from <table_name>;

Non esiste una riga come la 100th-200th, perché non si specifica un ORDER BY. L'ordine non è garantito se non includi ORDER BY per molte ragioni interessanti, ma non è proprio questo il punto.

Quindi, per illustrare il tuo punto, usiamo una tabella: userò la tabella Users dal dump dei dati Stack Overflow ed eseguirò questa query:

SELECT * FROM dbo.Users ORDER BY DisplayName;

Per impostazione predefinita, non esiste alcun indice nel campo DisplayName, quindi SQL Server deve eseguire la scansione dell'intera tabella, quindi ordinarlo per DisplayName. Ecco il piano di esecuzione :

Scansione indice cluster con un ordinamento

Non è carino - è un sacco di lavoro, con un costo sottotetto stimato di circa 30k. (Puoi vederlo passando il mouse sopra l'operatore di selezione su PasteThePlan.) Quindi cosa succede se vogliamo solo le righe 100-200? Possiamo usare questa sintassi in SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

Anche il piano di esecuzione è piuttosto brutto:

Scansione indice cluster con un ordinamento e una parte superiore

SQL Server sta ancora eseguendo la scansione dell'intera tabella per creare l'elenco ordinato solo per darti le tue righe 100-200 e il costo è ancora di circa 30k. Ancora peggio, l'intero elenco verrà ricostruito ogni volta che viene eseguita la query (perché, dopo tutto, qualcuno potrebbe aver cambiato il suo DisplayName.)

Per renderlo più veloce, possiamo creare un indice non cluster su DisplayName, che è una copia della nostra tabella, ordinata per quel campo specifico:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

Con quell'indice, il piano di esecuzione della nostra query ora cerca un indice:

Ricerca indice e ricerca chiave

La query termina all'istante e ha un costo di sottostruttura stimato di appena 0,66 (rispetto a 30k).

In breve, se organizzi i dati in modo da supportare le query che esegui frequentemente, quindi sì, SQL Server può prendere scorciatoie per velocizzare le tue query. Se, d'altra parte, tutto ciò che hai sono cumuli o indici raggruppati, sei fregato.


"Per impostazione predefinita, non esiste alcun indice nel campo DisplayName, quindi SQL Server deve eseguire la scansione dell'intera tabella, quindi ordinarla per DisplayName." Mi scusi se questa è una domanda molto semplice - nel caso in cui ho citato la tua risposta, quando ha detto "Scansiona l'intera tabella", significa che tutti i dati vengono portati in memoria e ordinati (che non sembra nel modo giusto)?
AV94

Dalla tua risposta, capisco che se il campo è indicizzato, quindi fare query come: ottenere dalla 100a alla 200a riga è molto efficiente poiché SQL cerca l'indice (albero B, ecc.) E va direttamente a quel punto (100a fila). Potresti dirmi se questa è la giusta comprensione?
AV94

@AnilVedala sulla tua prima domanda - sì, i dati devono essere ordinati. In quale altro modo un database potrebbe farlo con un elenco non ordinato?
Brent Ozar,

1
@AnilVedala sulla tua seconda domanda - ecco dove arriva l'ultimo piano di esecuzione che ti ho dato. (Se stai chiedendo come leggere un piano di esecuzione, prendi il libro Piani di esecuzione di Grant Fritchey.)
Brent Ozar

15

Proprio come un'aggiunta alla risposta di Brent quando si utilizza un indice non di copertura per evitare un ordinamento, esiste un potenziale problema con i numeri di pagina successivi che possono essere visualizzati eseguendo il seguito

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

Il piano di esecuzione mostra che la ricerca è stata eseguita 100.100 volte anche se tutte le righe tranne 100 vengono quindi filtrate dall'operatore TOP.

inserisci qui la descrizione dell'immagine

Questo può essere mitigato usando il modello qui sotto

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Questo filtra tutti tranne le ultime 100 righe prima di eseguire le ricerche che possono avere un impatto significativo sulla velocità per grandi valori di offset.

inserisci qui la descrizione dell'immagine


3

Dipende molto da come si implementa l'impaginazione all'interno della query, dalla natura dei dati e dal modo in cui è configurato il sistema. È abbastanza sicuro affermare che SQL Server tenterà di restituire i dati utilizzando quello che ritiene sia il minor sforzo possibile. Se non si dispone di ordinamenti, filtri, raggruppamenti o finestre espliciti, è possibile che SQL Server possa ottimizzare il piano di query in modo da poter restituire solo le pagine dal disco che contenevano i dati richiesti dalla query, o meglio, direttamente dal pool di buffer. Non appena si inizia a modificare la query per includere l'ordinamento, il raggruppamento, il windowing e il filtro, allora inizia a complicarsi.

C'è un ottimo articolo sulle prestazioni SQL qui che fornisce alcuni dettagli sui vari metodi di impaginazione e su come influenzano il piano di query. Consiglio vivamente di leggerlo e quindi provare alcuni dei vari metodi che sottolineano e vedere quale piano di query è scelto sul proprio sistema.

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.