Dato che sono un giovane sviluppatore e non sono molto abile nell'uso dei database (PostgreSQL 9.3) ho riscontrato alcuni problemi con un progetto, in cui ho davvero bisogno di aiuto.
Il mio progetto riguarda la raccolta di dati dai dispositivi (fino a 1000 o più dispositivi), in cui ogni dispositivo invia un blocco di dati al secondo, il che rende circa 3 milioni di righe all'ora.
Attualmente ho una grande tabella in cui memorizzo i dati in arrivo di ogni dispositivo:
CREATE TABLE data_block(
id bigserial
timestamp timestamp
mac bigint
)
Poiché esistono diversi tipi di dati che un blocco di dati può (o non può) includere, ci sono altre tabelle che fanno riferimento alla data_blocktabella.
CREATE TABLE dataA(
data_block_id bigserial
data
CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...
È possibile che in un blocco dati vi siano 3 dati A, 1 dati B, ma nessun dato C.
I dati verranno conservati per alcune settimane, quindi avrò ~ 5 miliardi di righe in questa tabella. Al momento, ho ~ 600 milioni di righe nella tabella e le mie query impiegano molto tempo. Così ho deciso di creare un indice timestampe mac, poiché le mie dichiarazioni selezionate richiedono sempre una query nel tempo e spesso anche nel tempo + mac.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... ma le mie domande richiedono ancora secoli. Ad esempio, ho richiesto i dati per un giorno e un mac:
SELECT * FROM data_block
WHERE timestamp>'2014-09-15'
AND timestamp<'2014-09-17'
AND mac=123456789
Index Scan using index_ts_mac on data_block (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms
Ho fatto un vuoto completo prima dell'esecuzione della query. Esiste un modo elegante per risolvere un simile problema con tabelle di grandi dimensioni per eseguire una query <10 sec?
Ho letto del partizionamento, ma questo non funzionerà con i miei dati dataA, dataB, datiC riferimenti a data_block_id giusto? Se funzionasse in qualche modo, dovrei creare partizioni nel tempo o su Mac?
Ho cambiato il mio indice nell'altra direzione. Prima MAC, poi timestamp, e guadagna molte prestazioni.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Tuttavia, le query richiedono> 30 sec. Soprattutto quando faccio un LEFT JOINcon le mie tabelle di dati. Ecco una EXPLAIN ANALYZEdelle query con il nuovo indice:
EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
-> Bitmap Index Scan on index_mac_ts (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms
Purtroppo il mio hardware è strettamente limitato. Sto usando un Intel i3-2100 a 3.10 Ghz, 4 GB di RAM. Le mie impostazioni attuali sono le seguenti:
default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2