Como sou um desenvolvedor jovem e não sou muito habilidoso no uso de bancos de dados (PostgreSQL 9.3), tive alguns problemas com um projeto, onde realmente precisava de ajuda.
Meu projeto é sobre a coleta de dados de dispositivos (até 1000 ou mais), em que cada dispositivo envia um bloco de dados por segundo, o que gera cerca de 3 milhões de linhas por hora.
Atualmente, tenho uma tabela grande onde armazeno os dados recebidos de cada dispositivo:
CREATE TABLE data_block(
id bigserial
timestamp timestamp
mac bigint
)
Como existem vários tipos de dados que um bloco de dados pode (ou não pode) incluir, existem outras tabelas que referenciam a data_block
tabela.
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);
...
É possível que em um bloco de dados haja 3x dadosA, 1x dadosB, mas nenhum dadoC.
Os dados serão mantidos por algumas semanas, então eu vou ter ~ 5 bilhões de linhas nesta tabela. No momento, tenho ~ 600 milhões de linhas na tabela e minhas consultas demoram muito tempo. Decidi fazer um índice over timestamp
e mac
, porque minhas instruções select sempre consultam ao longo do tempo e, muitas vezes, também ao longo do tempo + mac.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... mas minhas consultas ainda levam anos. Por exemplo, consultei dados para um dia e um 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
Fiz um vácuo total antes da execução da consulta. Existe uma maneira elegante de resolver esse problema com grandes tabelas para fazer uma consulta <10seg?
Eu li sobre particionamento, mas isso não funcionará com minhas referências de dataA, dataB, dataC a data_block_id, certo? Se funcionasse de alguma forma, devo fazer partições com o tempo ou com o mac?
Mudei meu índice para outra direção. Primeiro MAC, depois carimbo de data e hora, e obtém muito desempenho.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Ainda assim, as consultas demoram> 30s. Especialmente quando eu faço um LEFT JOIN
com minhas tabelas de dados. Aqui está uma EXPLAIN ANALYZE
consulta com o novo índice:
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
Infelizmente meu hardware é estritamente limitado. Estou usando um Intel i3-2100 @ 3.10Ghz, 4GB RAM. Minhas configurações atuais são as seguintes:
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