Query lente su indice // tabella miliardi di righe utilizzate


10

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

Risposte:


1

Questo potrebbe riflettere la mia propensione per MS SQL, ma proverei a raggruppare la tabella in base a timestamp. Se stai spesso estraendo dati per un periodo di tempo specifico, questo ti aiuterà perché i dati verranno archiviati fisicamente contigui. Il sistema può cercare il punto iniziale, scansionare fino alla fine dell'intervallo ed eseguire. Se stai eseguendo una query per un'ora specifica, sono solo 3.600.000 di record.

Se la tua query (che è ...?) Riguarda una macchina specifica, Postgres dovrà quindi filtrare il 99,9% di quei record di 3,6 M. Se questo filtro uno su mille è più selettivo di un tipico adattatore di intervalli di date, è necessario utilizzare il maccampo più selettivo come primo componente dell'indice. Potrebbe comunque valere la pena raggruppare.

Se ancora non lo fa, partizionerei per lo stesso campo che stai indicizzando, o timestampo mac.

Non hai fornito i tipi di dati. Sono adeguati ai dati? La memorizzazione delle date come testo gonfia inutilmente la tabella, ad esempio.


2
Postgres non ha indici raggruppati (sebbene possa raggruppare una tabella lungo un indice - ma ciò deve essere fatto manualmente e non "rimarrà")
a_horse_with_no_name

Grazie per il consiglio. ora funziona più velocemente di prima, ma con prestazioni molto basse> 30 secondi per query. ho anche fatto il clustering, ma come ha detto @a_horse_with_no_name: in Postgres questo è un colpo solo. i miei tipi di dati sono giusti, credo. li ho aggiunti alla domanda
manman,

Senza tabelle raggruppate, la mia prossima raccomandazione per le query di intervallo sarebbe il partizionamento.
Jon of All Trades,

-2

Ho lavorato su un'applicazione che aveva miliardi di letture da contatori elettrici ed ho eseguito la maggior parte delle query in meno di 10 secondi.

Il nostro ambiente era diverso. Microsoft SQL Server su un computer di classe server (4 core, 24 GB di memoria). Qualche possibilità di passare a un server?

Un grosso problema è che l'ingestione delle letture una alla volta ha avuto un grande impatto sulle prestazioni del database. La scrittura dei dati richiesti richiede blocchi e query. Puoi fare inserti in batch?

Con il tuo schema, avrai 4 tabelle molto grandi. Sarà importante che tutti i tuoi join utilizzino gli indici su entrambe le tabelle. Una scansione della tabella richiederà per sempre. È possibile unirli in 1 tabella con campi null null?


inserimenti in batch: potrei eseguire inserimenti di massa ma al momento sto lavorando su un database di test, in cui non viene effettuato alcun inserimento mentre è in esecuzione una query. ma grazie ci penserò più tardi :) indici: ho indici su ogni tavolo. sulle tabelle dei dati un indice sull'id, sulla tabella data_block su (mac, timestamp). il problema è presente anche durante la ricerca di dati A per join sinistro ma non esiste. anche con l'indice cerca nelle tabelle dei dati. campi nullable: non sono possibili perché un data_block può avere più di un dato di un tipo. 1xdata_block -> 4xdataA ad es.
manman

Il tuo strumento DB ti offre un analizzatore di query? Potrebbe essere necessario un indice su data_block basato su id.
KC-NH,

ci proverò, ma non capisco perché questo possa aiutare !?
manman,

-2

Stai rispettando i limiti di scalabilità intrinseci di Postgres (o di qualsiasi altro RDBMS).

Ricorda che un indice RDBMS è un B-Tree. Un albero a B è O (log n) sia nel caso medio che nel caso peggiore. Questo lo rende una scelta piacevole, sicura e prevedibile per valori ragionevoli di N. Si rompe quando N diventa troppo grande.

I database NoSQL sono (per la maggior parte) tabelle hash. Una tabella hash è O (1) nel caso medio e O (n) nel caso peggiore. Supponendo che tu possa evitare il caso peggiore, funziona davvero bene per valori molto grandi di N.

Inoltre, una tabella hash è facile da parallelizzare e un albero b no. Ciò rende le tabelle hash più adatte per un'architettura di elaborazione distribuita.

Quando inizi a raggiungere miliardi di tabelle di righe, è tempo di prendere in considerazione il passaggio da RDBMS a NoSQL. Cassandra sarebbe probabilmente una buona scelta per il tuo caso d'uso.


2
Molti RDBMS hanno molte più opzioni rispetto agli indici B-tree (hash, bitmap e altri). Alcuni DBMS stanno memorizzando righe e alcuni stanno memorizzando colonne. E O (logn) non è male, anche per miliardi di righe. E non possono avere alcun limite quando stanno usando una macchina da 4 GB di memoria.
ypercubeᵀᴹ
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.