2015-09-09 12 views
5

Di seguito è stata richiesta una query per circa 15 secondi per restituire i dati nonostante l'indice e id come chiave primaria.L'ordine per query sulla colonna timestamp è molto lento

select id from my_table order by insert_date offset 0 limit 1 

Il spiegano analizzare è come qui sotto

"Limit (cost=1766417.72..1766417.72 rows=1 width=12) (actual time=32479.440..32479.441 rows=1 loops=1)" 
" -> Sort (cost=1766417.72..1797117.34 rows=12279848 width=12) (actual time=32479.437..32479.437 rows=1 loops=1)" 
"  Sort Key: insert_date" 
"  Sort Method: top-N heapsort Memory: 25kB" 
"  -> Seq Scan on my_table (cost=0.00..1705018.48 rows=12279848 width=12) (actual time=0.006..21338.401 rows=12108916 loops=1)" 
"Total runtime: 32479.476 ms" 

Il mio tavolo ha poche altre colonne. Ma il tipo per la insert_date è

insert_date timestamp without time zone NOT NULL DEFAULT now(), 

Ho un indice su quella particolare colonna della data che è

CREATE INDEX my_table_insert_date_indx 
    ON my_table 
    USING btree 
    (insert_date) 
TABLESPACE somexyz_idx_ts; 

pochi valori da postgresql.conf di file:

shared_buffers = more than 1GB ## just for an example 
temp_buffers = more than 1GB 
work_mem = more than 1GB 
maintenance_work_mem = more than 1GB 
dynamic_shared_memory_type = posix 
default_statistics_target = 10000 
autovacuum = on 
random_page_cost = 2.0 
cpu_index_tuple_cost = 0.0005 

Sto usando Postgres 9.3 proprio adesso.

AGGIORNAMENTO ::

Ho appena eseguito la query di seguito qualche tempo fa:

select insert_date, count(*) from my_table group by insert_date 

e la parte superiore alcuni da risultato è:

"2015-04-02 00:00:00";3718104 
"2015-04-03 00:00:00";6410253 
"2015-04-04 00:00:00";538247 
"2015-04-05 00:00:00";1228877 
"2015-04-06 00:00:00";131248 

ho intorno 12 milioni di record su quel tavolo. E il conteggio sopra è quasi vicino a quel totale.

Non sicuro, ma potrebbe essere un problema che l'indice sia stato creato su una colonna che ha tonnellate di valori duplicati? Se è vero, allora abbiamo un modo per aggirare?

+0

Forse un posto migliore per chiedere: [dba.stackexchange.com] (http://dba.stackexchange.com) –

+0

C'era una domanda simile di recente su SO, e credo che la conclusione sarebbe stata che 9.4 era meglio ordinare da una colonna indicizzata per evitare un ordinamento. Potrebbe piacere cercare quella domanda. –

+0

Si prega di testare la stessa query con 'set enable_seqscan = off;' e mostra spiegare analizzare l'output. Quanto è grande il tuo indice e il tuo tavolo? '\ di + my_table_insert_date_indx', i comandi' \ dt + my_table' in psql mostreranno le taglie – alexius

risposta

2

La tua query viene eseguita circa 160000 volte più velocemente sul mio computer utilizzando sia PostgreSQL 9.3 che 9.4. La mia macchina non è niente di speciale.

-- From PostgreSQL 9.4; 9.3 is similar. 
show shared_buffers; -- 128MB 
show temp_buffers; -- 8MB 
show work_mem; -- 4MB 
show maintenance_work_mem; -- 64MB 
show dynamic_shared_memory_type; -- posix 
show default_statistics_target; -- 100 
show autovacuum; -- on 
show random_page_cost; -- 4 
show cpu_index_tuple_cost; -- 0.005 

Preparazione

Costruiamo un tavolo. (Avresti dovuto farlo nella tua domanda.)

create table my_table (
    id serial primary key, 
    insert_date timestamp not null 
); 

-- Round numbers of rows. 
insert into my_table(insert_date) 
select timestamp '2015-04-02 00:00:00' 
from generate_series(1, 3000000) n; 

insert into my_table(insert_date) 
select timestamp '2015-04-03 00:00:00' 
from generate_series(1, 6000000) n; 

insert into my_table(insert_date) 
select timestamp '2015-04-04 00:00:00' 
from generate_series(1, 500000) n; 

insert into my_table(insert_date) 
select timestamp '2015-04-05 00:00:00' 
from generate_series(1, 1200000) n; 

insert into my_table(insert_date) 
select timestamp '2015-04-06 00:00:00' 
from generate_series(1, 131000) n; 

Creare un indice e aggiornare le statistiche.

create index on my_table (insert_date); 
analyze my_table; 

PostgreSQL 9.4

Ora, che tipo di piano di esecuzione otteniamo dalla tua prima domanda?

explain analyze 
select id from my_table order by insert_date offset 0 limit 1; 
 
"Limit (cost=0.43..0.48 rows=1 width=12) (actual time=0.014..0.014 rows=1 loops=1)" 
" -> Index Scan using my_table_insert_date_idx on my_table (cost=0.43..540656.27 rows=11200977 width=12) (actual time=0.012..0.012 rows=1 loops=1)" 
"Planning time: 0.195 ms" 
"Execution time: 0.032 ms" 

PostgreSQL 9.3

explain analyze 
select id from my_table order by insert_date offset 0 limit 1; 
 
"Limit (cost=0.43..0.47 rows=1 width=12) (actual time=0.058..0.059 rows=1 loops=1)" 
" -> Index Scan using my_table_insert_date_idx on my_table (cost=0.43..339814.36 rows=10830995 width=12) (actual time=0.057..0.057 rows=1 loops=1)" 
"Total runtime: 0.098 ms" 

Vostri criteri

select id from my_table order by insert_date offset 0 limit 1; 

è indeterminata. Ci sono 3 milioni di righe che hanno l'insert_date più basso (la data che apparirà per prima, secondo la clausola ORDER BY). Scegli uno di quei 3 milioni. PostgreSQL non garantisce che otterrai lo stesso id ogni volta.

Se non ti interessa quale dei 3 milioni di ID restituiti, puoi esprimere la query in modo diverso. Ma non penso che esprimerlo diversamente ti darà 160k volte di accelerazione.

Alcune delle impostazioni incluse possono essere modificate per una particolare query. Quindi puoi fare qualcosa di simile.

-- Don't commit or rollback . . . 
begin transaction; 
set local work_mem = '8 MB'; 

explain analyze 
select id from my_table order by insert_date offset 0 limit 1; 
-- Displays the result. 

Esegui o arresta manualmente.

commit; 

L'impostazione work_mem restituisce il valore impostato all'avvio del server.

show work_mem; -- 4MB