PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

La query di selezione con limite di offset è troppo lenta

È lento perché deve individuare l'offset superiore righe ed esegui la scansione delle 100 successive. Nessuna quantità di ottimizzazione cambierà la situazione quando hai a che fare con enormi offset.

Questo perché la tua query letteralmente istruisci il motore DB per visitare molte righe utilizzando offset 3900000 -- sono 3,9 milioni di righe. Le opzioni per accelerare un po' non sono molte.

Aiuteranno RAM super veloci, SSD, ecc. Ma in tal modo guadagnerai solo di un fattore costante, il che significa che è semplicemente calciare la lattina lungo la strada finché non raggiungi un offset sufficientemente grande.

Garantire che la tabella rientri nella memoria, con molto altro da risparmiare, aiuterà anche con un fattore costante più grande, tranne la prima volta. Ma questo potrebbe non essere possibile con una tabella o un indice sufficientemente grande.

Garantire che stai eseguendo scansioni solo indice funzionerà in una certa misura. (Vedi la risposta di Velis; ha molti meriti.) Il problema qui è che, per tutti gli scopi pratici, puoi pensare a un indice come a una tabella che memorizza una posizione del disco e i campi indicizzati. (È più ottimizzato di così, ma è una prima approssimazione ragionevole.) Con un numero sufficiente di righe, continuerai a riscontrare problemi con un offset sufficientemente grande.

Anche cercare di memorizzare e mantenere la posizione precisa delle file è destinato a essere un approccio costoso. (Ciò è suggerito ad es. da Benjist.) Sebbene tecnicamente fattibile, soffre di limitazioni simili a quelle derivanti dall'uso di MPTT con una struttura ad albero:guadagnerai significativamente nelle letture ma finirai con tempi di scrittura eccessivi quando un nodo viene inserito, aggiornato o rimosso in modo tale che grandi porzioni di dati debbano essere aggiornate insieme.

Come si spera sia più chiaro, non c'è una vera bacchetta magica quando hai a che fare con offset così grandi. Spesso è meglio considerare approcci alternativi.

Se stai impaginando in base all'ID (o un campo data o qualsiasi altro insieme di campi indicizzabili), un potenziale trucco (utilizzato da blogspot, ad esempio) sarebbe quello di far iniziare la tua query in un punto arbitrario nell'indice.

In altre parole, invece di:

example.com?page_number=[huge]

Fai qualcosa come:

example.com?page_following=[huge]

In questo modo, tieni traccia di dove ti trovi nel tuo indice e la query diventa molto veloce perché può dirigersi direttamente al punto di partenza corretto senza passare attraverso un miliardo di righe:

select * from foo where ID > [huge] order by ID limit 100

Naturalmente, perdi la capacità di saltare ad es. pagina 3000. Ma pensaci in modo onesto:quand'è stata l'ultima volta che sei passato a un numero enorme di pagina su un sito invece di andare direttamente ai suoi archivi mensili o usando la sua casella di ricerca?

Se stai impaginando ma vuoi mantenere l'offset della pagina in qualsiasi modo, un altro approccio è vietare l'uso di un numero di pagina più grande. Non è sciocco:è quello che sta facendo Google con i risultati di ricerca. Quando si esegue una query di ricerca, Google fornisce una stima del numero di risultati (puoi ottenere un numero ragionevole utilizzando explain ), e quindi ti permetterà di sfogliare le prime migliaia di risultati, niente di più. Tra le altre cose, lo fanno per motivi di prestazioni, proprio quello che stai incontrando.