Como soy un desarrollador joven y no soy realmente experto en el uso de bases de datos (PostgreSQL 9.3), tuve algunos problemas con un proyecto, en el que realmente necesito ayuda.
Mi proyecto se trata de recopilar datos de dispositivos (hasta 1000 o más dispositivos), donde cada dispositivo envía un bloque de datos por segundo, lo que genera aproximadamente 3 millones de filas por hora.
Actualmente tengo una gran tabla donde almaceno los datos entrantes de cada dispositivo:
CREATE TABLE data_block(
id bigserial
timestamp timestamp
mac bigint
)
Debido a que hay varios tipos de datos que un bloque de datos puede incluir (o no), hay otras tablas que hacen referencia a la data_block
tabla.
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);
...
Es posible que en un bloque de datos haya 3 datos A, 1 datos B, pero no datos C.
Los datos se conservarán durante algunas semanas, por lo que tendré ~ 5 mil millones de filas en esta tabla. Por el momento, tengo ~ 600 millones de filas en la tabla y mis consultas toman mucho tiempo. Así que decidí hacer un índice timestamp
y mac
, porque mis declaraciones de selección siempre consultan con el tiempo y, a menudo, también con el tiempo + mac.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... pero mis consultas todavía llevan años. Por ejemplo, consulté datos de un día y 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
Hice un vacío completo antes de ejecutar la consulta. ¿Hay una manera elegante de resolver un problema con tablas grandes para hacer una consulta <10 segundos?
Leí sobre particiones, pero esto no funcionará con mis referencias dataA, dataB, dataC a data_block_id ¿verdad? Si funcionara de alguna manera, ¿debería hacer particiones con el tiempo o en mac?
Cambié mi índice a la otra dirección. Primero MAC, luego marca de tiempo, y gana mucho rendimiento.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Pero aún así, las consultas toman> 30 segundos. Especialmente cuando hago un LEFT JOIN
con mis tablas de datos. Aquí está una EXPLAIN ANALYZE
de las consultas con el nuevo í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
Lamentablemente mi hardware es estrictamente limitado. Estoy usando un Intel i3-2100 @ 3.10Ghz, 4GB RAM. Mi configuración actual es la siguiente:
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