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
Trabajé en una aplicación que tenía miles de millones de lecturas de medidores eléctricos y ejecuté la mayoría de las consultas en menos de 10 segundos.
Nuestro ambiente era diferente. Microsoft SQL Server en una máquina de clase de servidor (4 núcleos, memoria de 24 GB). ¿Alguna posibilidad de actualizar a un servidor?
Un gran problema es que la ingesta de las lecturas de una en una tuvo un gran impacto en el rendimiento de la base de datos. Escribir datos requiere bloqueos y consultas esperarían. ¿Se pueden hacer insertos en lotes?
Con su esquema, tendrá 4 tablas muy grandes. Será importante que todas sus uniones usen índices en ambas tablas. Un escaneo de tabla tomará una eternidad. ¿Es factible fusionarlos en 1 tabla con campos nulos?
fuente
Está alcanzando los límites de escalabilidad inherentes de Postgres (o cualquier otro RDBMS).
Recuerde que un índice RDBMS es un B-Tree. Un árbol B es O (log n) tanto para el promedio como para el peor de los casos. Esto lo convierte en una opción agradable, segura y predecible para valores razonables de N. Se descompone cuando N es demasiado grande.
Las bases de datos NoSQL son (en su mayor parte) tablas hash. Una tabla hash es O (1) en el caso promedio y O (n) en el peor de los casos. Suponiendo que puede evitar el peor de los casos, funciona muy bien para valores muy grandes de N.
Además, una tabla hash es fácil de paralelizar y un árbol b no lo es. Esto hace que las tablas hash sean más adecuadas para una arquitectura informática distribuida.
Cuando comience a obtener miles de millones de tablas de filas, es hora de considerar cambiar de RDBMS a NoSQL. Cassandra probablemente sería una buena opción para su caso de uso.
fuente