AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / user-263417

Stepan Parunashvili's questions

Martin Hope
Stepan Parunashvili
Asked: 2024-12-10 07:07:25 +0800 CST

A consulta do gráfico do Postgres fica lenta devido a uma junção de loop aninhado

  • 6

Contexto

Estou usando o postgres como um banco de dados gráfico multilocatário.

Todos os dados são armazenados em uma única tabela e eu confio em índices parciais para tornar as consultas eficientes.

Veja como é um esquema mínimo:

CREATE TABLE triples (
    app_id text NOT NULL,
    entity_id text NOT NULL,
    attr_id text NOT NULL,
    value jsonb NOT NULL,
    eav boolean NOT NULL DEFAULT false,
    ave boolean NOT NULL DEFAULT false,
    vae boolean NOT NULL DEFAULT false,
    created_at bigint NOT NULL DEFAULT 0,
    checked_data_type text
);

CREATE OR REPLACE FUNCTION triples_extract_number_value(value jsonb)
RETURNS double precision AS $$
BEGIN
  IF jsonb_typeof(value) = 'number' THEN
    RETURN value::double precision;
  ELSE
    RETURN NULL;
  END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE INDEX vae_idx ON triples (app_id, value, attr_id, entity_id) WHERE vae;

CREATE INDEX ave_number_idx ON triples (app_id, attr_id, triples_extract_number_value(value), entity_id) WHERE ave AND checked_data_type = 'number';

Em um aplicativo com conversations, groups, e messages, os dados podem ser armazenados como:

{app_id: 'app_1', entity_id: 'convo_1', attr_id: 'title': "My Conversation"} 
{app_id: 'app_1', entity_id: 'group_1', attr_id: 'title': "My Group"} 
{app_id: 'app_1', entity_id: 'convo_1', attr_id: 'group': "'group_1'", vae: true} // this 'links' convos to groups
{app_id: 'app_1', entity_id: 'msg_1', attr_id: 'time': "123", ave: true, checked_data_type: 'number'} // this indexes our 'time' field  
{app_id: 'app_1', entity_id: 'msg_1', attr_id: 'convo': "'convo_1'", vae: true} // this  links `msg_1` to `convo_1`

Meta

Quero responder à seguinte questão:

"Dê-me todos convosos que pertencem ao group'grupo 1' e que têm um messagewho's timemaior que 5."

Para fazer isso, escrevi a seguinte consulta, que faz 3 autojunções:

SELECT 
    DISTINCT(match_0_0.entity_id)
FROM 
    triples AS match_0_0
JOIN 
    triples AS match_0_1
    ON match_0_1.app_id = match_0_0.app_id
    AND match_0_1.vae = true
    AND match_0_1.attr_id = 'convo'
    AND match_0_1.value = to_jsonb(match_0_0.entity_id)
JOIN 
    triples AS match_0_2
    ON match_0_2.app_id = match_0_1.app_id
    AND match_0_2.ave = true
    AND match_0_2.attr_id = 'time'
    AND triples_extract_number_value(match_0_2.value) >= 5
    AND match_0_2.checked_data_type = 'number'
    AND match_0_2.entity_id = match_0_1.entity_id
WHERE 
    match_0_0.app_id = 'chat_app'
    AND match_0_0.vae = true
    AND match_0_0.attr_id = 'groups'
    AND match_0_0.value = '"group_1"';

Problema

O problema é que essa consulta leva cerca de 14 segundos para ser satisfeita. Executando com EXPLAIN (ANALYZE, BUFFERS), mostra:

PLANO DE CONSULTA
Único (custo=1,10..51,92 linhas=1 largura=8) (tempo real=0,588..14786,024 linhas=150 loops=1)
Buffers: hit compartilhado=523495
-> Loop aninhado (custo=1,10..51,92 linhas=1 largura=8) (tempo real=0,588..14784,589 linhas=5996 loops=1)
Buffers: hit compartilhado=523495
-> Loop aninhado (custo=0,82..37,31 linhas=4 largura=25) (tempo real=0,048..29,058 linhas=12000 loops=1)
Buffers: hit compartilhado=1499
-> Index Only Scan usando vae_idx em triplos match_0_0 (custo=0,41..11,98 linhas=3 largura=17) (tempo real=0,029..0,533 linhas=300 loops=1)
Cond. de índice: ((app_id = 'chat_app'::texto) E (valor = '"grupo_1"'::jsonb) E (attr_id = 'grupos'::texto))
Buscas de pilha: 300
Buffers: hit compartilhado=55
-> Index Only Scan usando vae_idx em triplos match_0_1 (custo=0,41..8,44 linhas=1 largura=34) (tempo real=0,016..0,079 linhas=40 loops=300)
Índice Cond: ((app_id = 'chat_app'::text) E (valor = to_jsonb(match_0_0.entity_id)) E (attr_id = 'convo'::text))
Buscas de heap: 12000
Buffers: hit compartilhado=1444
-> Index Scan usando ave_number_idx em triplos match_0_2 (custo=0,28..3,64 linhas=1 largura=17) (tempo real=0,929..1,229 linhas=0 loops=12000)
Índice Cond: ((app_id = 'chat_app'::text) E (attr_id = 'time'::text) E (triples_extract_number_value(value) >= '5'::double precision) E (entity_id = match_0_1.entity_id))
Buffers: hit compartilhado=521996
Planejamento:
Buffers: hit compartilhado=109
Tempo de planejamento: 0,477 ms
Tempo de execução: 14786.197 ms

Loop aninhado

Observando EXPLAIN (ANALYZE, BUFFERS), notei que a junção nested_loop tem muitos acertos de buffer.

Se eu tentar desabilitar junções de loop aninhadas, a consulta será resolvida em 40 ms :

SET enable_nestloop TO off;

SELECT 
    DISTINCT(match_0_0.entity_id)
FROM 
    triples AS match_0_0
JOIN 
    triples AS match_0_1
    ON match_0_1.app_id = match_0_0.app_id
    AND match_0_1.vae = true
    AND match_0_1.attr_id = 'convo'
    AND match_0_1.value = to_jsonb(match_0_0.entity_id)
JOIN 
    triples AS match_0_2
    ON match_0_2.app_id = match_0_1.app_id
    AND match_0_2.ave = true
    AND match_0_2.attr_id = 'time'
    AND triples_extract_number_value(match_0_2.value) >= 5
    AND match_0_2.checked_data_type = 'number'
    AND match_0_2.entity_id = match_0_1.entity_id
WHERE 
    match_0_0.app_id = 'chat_app'
    AND match_0_0.vae = true
    AND match_0_0.attr_id = 'groups'
    AND match_0_0.value = '"group_1"';

Pergunta

Existe alguma maneira de dar uma dica ao postgres para que ele escolha uma estratégia melhor?

Reprodução

Configurei uma reprodução no DB Fiddle, que mostra a consulta lenta:

https://www.db-fiddle.com/f/4jyoMCicNSZpjMt4jFYoz5/15620

postgresql
  • 1 respostas
  • 99 Views

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve