2015-05-19 18 views
7

Ho una tabella semplice con la colonna singola CHIAVE PRIMARIA chiamata id, digitare serial. Ci sono esattamente 100.000.000 di file. La tabella accetta 48 GB, indice PK ca 2,1 GB. Il funzionamento della macchina è "dedicato" solo per Postgres ed è simile a Core i5, HDD da 500 GB, 8 GB di RAM. Pg config è stato creato dall'utilità pgtune (buffer condivisi ca 2 GB, cache effettiva ca 7 GB). OS è il server Ubuntu 14.04, Postgres 9.3.6.Perché sia ​​il conteggio SELECT (PK) che il conteggio SELECT (*) sono così lenti?

Perché entrambi sono SELECT count(id) e SELECT count(*) così lento in questo semplice caso (circa 11 minuti)?

Perché il planner PostgreSQL sceglie la scansione completa della tabella anziché la scansione dell'indice, che dovrebbe essere almeno 25 volte più veloce (nel caso in cui debba leggere l'intero indice dall'HDD). O dove sbaglio?

Btw eseguire la query più volte di seguito non modifica nulla. ancora cca 11 minuti.

Piano di esecuzione qui:

Aggregate (cost=7500001.00..7500001.01 rows=1 width=0) (actual time=698316.978..698316.979 rows=1 loops=1) 
    Buffers: shared hit=192 read=6249809 
    -> Seq Scan on transaction (cost=0.00..7250001.00 rows=100000000 width=0) (actual time=0.009..680594.049 rows=100000001 loops=1) 
     Buffers: shared hit=192 read=6249809 
Total runtime: 698317.044 ms 
+0

La quantità di dati da leggere è 25 volte più piccola e contiene tutte le chiavi che è sufficiente per contare, non lo è/non è vero? Ho eseguito sia VACUUM PIENO che ANALIZZA (che ha preso insieme più di 6 ore). – Kousalik

+0

Hai un sacco di DML simultaneo che si sta verificando su quel tavolo? L'indice può (e sarà) utilizzato solo se è affidabile. Se ci sono molte transazioni concorrenti (o transazioni non completate), Postgres potrebbe non scegliere di usare l'indice. Hai una connessione "idle in transaction" che ha modificato quella tabella? Inoltre, qual è il valore di 'random_page_cost' (http://www.postgresql.org/docs/current/static/runtime-config-query.html#GUC-RANDOM-PAGE-COST) tale impostazione influenzerà la tendenza dei pianificatori a usa un indice –

+0

Si potrebbe anche voler leggere questo: https://wiki.postgresql.org/wiki/Index-only_scans –

risposta

8

Considerando le specifiche di un HDD è di solito da qualche parte tra 50 Mb/s e 100 Mb/s poi per 48GB mi aspetterei di leggere tutto tra 500 e 1000.

Dato che non esiste una clausola where, il pianificatore vede che si è interessati alla maggior parte dei record, quindi non utilizza l'indice in quanto richiederebbe ulteriori indici. La ragione per cui postgresql non può usare l'indice si trova nel MVCC che postgresql usa per consistenza della transazione. Ciò richiede che le righe vengano tirate per garantire risultati accurati. (vedi,)

La cache, la CPU, ecc. non influiscono su questo né cambiano le impostazioni di memorizzazione nella cache. Questo è legato all'IO e la cache verrà completamente eliminata dopo la query.

se si può vivere con un'approssimazione si può utilizzare il campo reltuples nei metadati tabella:

SELECT reltuples FROM pg_class WHERE relname = 'tbl'; 

Dal momento che questo è solo una singola riga questo è velocissimo.

Aggiornamento: dal 9.2 un nuovo modo di memorizzare le informazioni sulla visibilità ha permesso che i conteggi dell'indice si ripetessero. Tuttavia ci sono alcuni avvertimenti, soprattutto nel caso in cui non ci sia un predicato per limitare le righe. vedi https://wiki.postgresql.org/wiki/Index-only_scans per maggiori dettagli.

+0

Beh, se voglio conteggi id, quindi la scansione solo indice dovrebbe essere sufficiente e sarebbe grande vincere perché è molto più piccolo. La velocità di lettura media è di quasi 100 MB/s quando viene letta in sequenza. La cosa che non capisco è perché il pianificatore non capisca che voglio contare solo il valore indicizzato. Non posso testare la stessa cosa in questo momento, ma sono abbastanza sicuro che Oracle potrebbe capire. Proverò con Oracle domani. – Kousalik

+0

Guardato dentro.Questo non può essere fatto a causa del sistema MVCC utilizzato da Postgres. Per ottenere conteggi precisi, è necessario eseguire la scansione della tabella. Altri database che utilizzano controlli di concorrenza diversi possono consentire questo. Sto aggiornando la mia risposta. –

+0

Fornisci anche la fonte, per favore. Grazie. – Kousalik