Consultas lentas na tabela de bilhões de linhas // índice usado


10

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

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 timestampe 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 JOINcom minhas tabelas de dados. Aqui está uma EXPLAIN ANALYZEconsulta 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

Respostas:


1

Isso pode refletir meu viés do MS SQL, mas eu tentaria agrupar a tabela por timestamp. Se você está frequentemente puxando dados por um período de tempo específico, isso ajudará porque os dados serão fisicamente armazenados de forma contígua. O sistema pode buscar o ponto inicial, digitalizar até o final do intervalo e concluir. Se você está consultando por uma hora específica, são apenas 3.600.000 registros.

Se sua consulta (que é ...?) For para uma máquina específica, o Postgres precisará filtrar 99,9% desses registros de 3,6 milhões. Se esse filtro de um em mil for mais seletivo do que um ajustador de período típico, use o maccampo mais seletivo como o primeiro componente do seu índice. Ainda pode valer a pena agrupar.

Se isso ainda não funcionar, particionaria pelo mesmo campo que você está indexando, seja timestampou mac.

Você não deu os tipos de dados. Eles são apropriados para os dados? Armazenar datas como texto inchará desnecessariamente sua mesa, por exemplo.


2
Postgres não ter agrupado índices (embora possa agrupar uma tabela ao longo de um índice - mas que precisa ser feito manualmente e não vai "ficar")
a_horse_with_no_name

Obrigada pelo conselho. agora ele roda mais rápido do que antes, mas ainda com um desempenho muito baixo> 30s por consulta. Eu também fiz cluster, mas como o @a_horse_with_no_name disse: no postgres, este é um tiro único. meus tipos de dados estão certos, eu acho. eu adicionei-los na questão
manman

Sem tabelas agrupadas, minha próxima recomendação para consultas por intervalo seria particionar.
Jon of All Trades

-2

Trabalhei em um aplicativo que tinha bilhões de leituras de medidores elétricos e executou a maioria das consultas em menos de 10 segundos.

Nosso ambiente era diferente. Microsoft SQL Server em uma máquina de classe de servidor (4 núcleos, 24 GB de memória). Alguma chance de atualizar para um servidor?

Um grande problema é que a ingestão das leituras, uma de cada vez, teve um grande impacto no desempenho do banco de dados. A gravação de dados exigia bloqueios e consultas aguardaria. Você pode fazer inserções em lotes?

Com seu esquema, você terá 4 tabelas muito grandes. Será importante que todas as suas junções usem índices nas duas tabelas. Uma varredura de tabela levará uma eternidade. É possível mesclá-los a 1 tabela com campos capazes nulos?


inserções em lotes: eu poderia fazer inserções em massa, mas no momento estou trabalhando em um banco de dados de teste, onde nenhuma inserção é feita enquanto a consulta está em execução. mas obrigado, vou pensar nisso mais tarde :) índices: tenho índices em todas as tabelas. nas tabelas de dados, um índice no ID, na tabela data_block em (mac, timestamp). o problema também existe quando estou procurando dadosA por junção esquerda, mas não há. mesmo com o índice, ele pesquisa nas tabelas de dados. campos anuláveis: não são possíveis porque um bloco de dados pode ter mais de um dado de um tipo. 1xdata_block -> 4xdataA por exemplo
manman

Sua ferramenta de banco de dados fornece um analisador de consultas? Você pode precisar de um índice em data_block com base no ID.
KC-NH

vou tentar, mas não entendo por que isso pode ajudar !?
manman

-2

Você está atingindo os limites de escalabilidade inerentes ao Postgres (ou qualquer outro RDBMS).

Lembre-se de que um índice RDBMS é uma árvore B. Uma árvore B é O (log n) para ambos os casos, médio e pior. Isso faz com que seja uma escolha agradável, segura e previsível para valores razoáveis ​​de N. É interrompido quando N fica muito grande.

Os bancos de dados NoSQL são (na maior parte) tabelas de hash. Uma tabela de hash é O (1) no caso médio e O (n) no pior caso. Supondo que você possa evitar o pior caso, ele funciona muito bem com valores muito grandes de N.

Além disso, é fácil paralelizar uma tabela de hash e uma árvore b não. Isso torna as tabelas de hash mais adequadas para uma arquitetura de computação distribuída.

Quando você começa a chegar a bilhões de tabelas de linhas, é hora de considerar mudar de RDBMS para NoSQL. Cassandra provavelmente seria uma boa escolha para o seu caso de uso.


2
Muitos RDBMS têm muito mais opções que índices da árvore B (hash, bitmap e outros). Alguns DBMS estão armazenando linhas e outros estão armazenando colunas. E O (logn) não é ruim, mesmo para bilhões de linhas. E eles não podem estar atingindo nenhum limite quando estão usando uma máquina de memória de 4 GB.
precisa saber é o seguinte
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.