Consultas lentas en billones-filas-tabla // índice utilizado

10

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_blocktabla.

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 timestampy 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 JOINcon mis tablas de datos. Aquí está una EXPLAIN ANALYZEde 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
manman
fuente

Respuestas:

1

Esto puede reflejar mi sesgo de MS SQL, pero trataría de agrupar la tabla timestamp. Si con frecuencia extrae datos durante un período de tiempo específico, esto ayudará porque los datos se almacenarán físicamente de forma contigua. El sistema puede buscar el punto de inicio, escanear hasta el final del rango y listo. Si está consultando por una hora específica, eso es solo 3,600,000 registros.

Si su consulta (¿cuál es ...?) Es para una máquina específica, Postgres necesitará filtrar el 99.9% de esos registros de 3.6 M. Si este filtro uno en mil es más selectivo que un ajustador de rango de fechas típico, debe usar el maccampo más selectivo como el primer componente de su índice. Todavía puede valer la pena agruparse.

Si eso todavía no lo hace, particionaría por el mismo campo que indizas, ya sea timestampo mac.

No diste los tipos de datos. ¿Son apropiados para los datos? Almacenar fechas como texto hinchará innecesariamente su tabla, por ejemplo.

Jon de todos los oficios
fuente
2
Postgres no tiene índices agrupados (aunque puede agrupar una tabla a lo largo de un índice, pero eso debe hacerse manualmente y no "permanecerá")
a_horse_with_no_name
Gracias por el consejo. ahora funciona más rápido que antes, pero aún con un rendimiento muy bajo> 30 segundos por consulta. También hice clustering, pero como dijo @a_horse_with_no_name: en postgres esto es de una sola vez. mis tipos de datos son correctos, creo. i les añadí en la pregunta
manman
Sin tablas agrupadas, mi próxima recomendación para consultas de rango sería la partición.
Jon of All Trades
-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?

KC-NH
fuente
inserciones en lotes: podría hacer inserciones masivas pero en este momento estoy trabajando en una base de datos de prueba, donde no se realizan inserciones mientras se ejecuta una consulta. pero gracias, pensaré en eso más tarde :) índices: tengo índices en cada tabla. en las tablas de datos, un índice en la identificación, en la tabla data_block en (mac, marca de tiempo). el problema también está presente cuando busco datos A por unión izquierda pero no. incluso con index busca las tablas de datos. campos anulables: no son posibles porque un bloque de datos puede tener más de un dato de un tipo. 1xdata_block -> 4xdataA eg
manman
¿Su herramienta DB le proporciona un analizador de consultas? Es posible que necesite un índice en data_block basado en la identificación.
KC-NH
Lo intentaré, ¡pero no entiendo por qué esto puede ayudar!
manman
-2

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.

Profesor Photon
fuente
2
Muchos RDBMS tienen muchas más opciones que los índices de árbol B (hash, mapa de bits y otros). Algunos DBMS almacenan filas y otros almacenan columnas. Y O (logn) no es malo, incluso para miles de millones de filas. Y no pueden alcanzar ningún límite cuando usan una máquina de memoria de 4GB.
ypercubeᵀᴹ