SELEZIONARE TOP 1 da una tabella molto grande su una colonna di indice è molto lento, ma non con ordine inverso ("desc")


17

Abbiamo un database di grandi dimensioni, circa 1 TB, che esegue SQL Server 2014 su un server potente. Tutto ha funzionato bene per alcuni anni. Circa 2 settimane fa abbiamo effettuato una manutenzione completa, che includeva: Installa tutti gli aggiornamenti software; ricostruire tutti gli indici e i file DB compatti. Tuttavia, non ci aspettavamo che a un certo punto l'utilizzo della CPU del DB aumentasse di oltre il 100% al 150% quando il carico effettivo era lo stesso.

Dopo un sacco di risoluzione dei problemi, l'abbiamo ridotto a una query molto semplice, ma non siamo riusciti a trovare una soluzione. La query è estremamente semplice:

select top 1 EventID from EventLog with (nolock) order by EventID

Ci vogliono sempre circa 1,5 secondi! Tuttavia, una query simile con "desc" richiede sempre circa 0 ms:

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTable ha circa 500 milioni di righe; EventIDè la colonna dell'indice cluster primario (ordinata ASC) con il tipo di dati bigint (colonna Identity). Esistono più thread che inseriscono i dati nella tabella in alto (EventID più grandi) e 1 thread elimina i dati dal basso (EventID più piccoli).

In SMSS, abbiamo verificato che le due query utilizzano sempre lo stesso piano di esecuzione:

  • Scansione indice cluster;

  • I numeri di riga stimati ed effettivi sono entrambi 1;

  • Il numero stimato ed effettivo di esecuzioni sono entrambi 1;

  • Il costo I / O stimato è 8500 (sembra essere elevato)

  • Se eseguito consecutivamente, il costo della query è lo stesso 50% per entrambi.

Ho aggiornato le statistiche dell'indice with fullscan, il problema persisteva; Ho ricostruito di nuovo l'indice e il problema sembrava essere scomparso per mezza giornata, ma è tornato.

Ho attivato le statistiche IO con:

set statistics io on

quindi ha eseguito le due query consecutive e ha trovato le seguenti informazioni:

(Per la prima query, quella lenta)

Tabella "PTable". Conteggio scansioni 1, letture logiche 407670, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

(Per la seconda query, quella veloce)

Tabella "PTable". Conteggio scansioni 1, letture logiche 4, letture fisiche 0, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Nota l'enorme differenza nelle letture logiche. L'indice viene utilizzato in entrambi i casi.

La frammentazione dell'indice potrebbe spiegare un po ', ma credo che l'impatto sia molto piccolo; e il problema non si è mai verificato prima. Un'altra prova è se eseguo una query come:

select * from EventLog with (nolock) where EventID=xxxx   

Anche se imposto xxxx sugli EventID più piccoli nella tabella, la query è sempre velocissima.

Abbiamo verificato e non vi sono problemi di blocco / blocco.

Nota: ho appena provato a semplificare il problema sopra. Il "PTable" è in realtà "EventLog"; l' PIDè EventID.

Ottengo lo stesso risultato test senza il NOLOCKsuggerimento.

Qualcuno può aiutare?

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Piani di esecuzione delle query più dettagliati in XML come segue:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

Non credo sia importante fornire l'istruzione create table. È un vecchio database e funziona perfettamente da molto tempo fino alla manutenzione. Abbiamo fatto molte ricerche da soli e le abbiamo ristrette alle informazioni fornite nella mia domanda.

La tabella è stata creata normalmente con la EventIDcolonna come chiave primaria, che è una identitycolonna di tipo bigint. In questo momento, immagino che il problema sia con la frammentazione dell'indice. Subito dopo la ricostruzione dell'indice, il problema sembrava essere scomparso per mezza giornata; ma perché è tornato così in fretta ...?

Risposte:


18

La scansione dell'indice cluster mostra 423.723 letture logiche per restituire la prima riga, impiegando 1926 ms:

NOCCIOLINE

Sembra piuttosto molto individuare la prima riga in ordine di indice.

Molto probabilmente l' attività di pulizia dei fantasmi è in corso molto indietro o si è interrotta. È necessario verificare l' ghost_record_countindice cluster in sys.dm_db_index_physical_statse monitorare le modifiche nel tempo.

La scansione ordinata dalla fine dell'indice che sta vedendo un'attività di eliminazione costante deve eseguire la scansione su un sacco di record fantasma prima di trovare la prima riga "viva" da restituire. Questo spiega le letture logiche extra. Una ricerca nel b-tree fino al valore più basso dell'indice incontrerà un numero molto inferiore di record fantasma.

Un altro fattore che influenza le prestazioni è che la scansione stessa diventa responsabile della rimozione dei record fantasma, come menzionato in Inside the Storage Engine: Ghost cleanup in profondità di Paul Randal.

È necessario verificare che il flag di traccia 661 (disabilita pulizia fantasma) non sia attivo.

soluzioni

Se il processo di pulizia fantasma si è arrestato completamente, la soluzione più efficace è normalmente riavviare l'istanza di SQL Server. È inoltre necessario assicurarsi che SQL Server stia eseguendo uno degli ultimi aggiornamenti cumulativi. Ci sono stati molti bug di pulizia dei fantasmi negli anni.

Nel tuo caso specifico:

Si è scoperto che il problema era causato da un altro database di test sullo stesso server. Quel database di test è stato ripristinato con "perdita di dati" ed è corrotto. Sorprendentemente, il processo di pulizia dei fantasmi apparentemente era bloccato in quel database. Una volta eliminato quel database danneggiato da SMSS, il problema si è risolto da solo (impiegando molto tempo e potrebbe aver causato il blocco del DB per un breve periodo).

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.