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 / dba / Perguntas / 332080
Accepted
kanders84152
kanders84152
Asked: 2023-10-13 01:27:12 +0800 CST2023-10-13 01:27:12 +0800 CST 2023-10-13 01:27:12 +0800 CST

No Postgres, como posso recuperar as N linhas principais de cada partição de classificação sem classificar a tabela inteira?

  • 772

Resumo

Tenho uma tabela com fontes de eventos e uma tabela com eventos. Preciso de uma consulta que me forneça os Neventos mais recentes de cada fonte ( Nestá entre 1 e 100). Atualmente, estou fazendo isso com uma subconsulta que executa uma ROW_NUMBER() OVER (PARTITION BY "EventSourceId" ORDER BY ...) as rankRecentconsulta externa que filtra WHERE rankRecent <= @N.

Os EXPLAIN ANALYZEresultados dizem que ele está usando meu índice para as cláusulas partitione order by, mas ainda está classificando a tabela inteira e aparentemente esperando encontrar 6 milhões de resultados, mas existem apenas 22 mil. Estou tentando descobrir se existe: (1) uma maneira melhor de obter os Neventos mais recentes para cada fonte de evento ou (2) uma maneira de sugerir ao planejador de consultas que ele não precisa classificar estritamente a maioria dos tabela, pois apenas as primeiras entradas serão usadas.

Além disso, há um segundo caso de uso para a consulta que nem sei como começar a indexar. Esse não é o cerne desta questão; Menciono-o apenas no interesse de incluir tudo o que possa ser relevante.

Detalhes

Configuração de dados

CREATE TABLE "EventSources"
(
    "Id" uuid NOT NULL,
    "Name" character varying(100),
    CONSTRAINT "PK_EventSources" PRIMARY KEY ("Id")
);

CREATE TABLE "Events"
(
    "Id" uuid NOT NULL,
    "EventSourceId" uuid NOT NULL,
    "Time" timestamp with time zone,
    "AltKey" character varying(100),
    CONSTRAINT "PK_Events" PRIMARY KEY ("Id"),
    CONSTRAINT "FK_Events_EventSources_EventSourceId" FOREIGN KEY ("EventSourceId") REFERENCES "EventSources" ("Id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE RESTRICT
);

CREATE INDEX "IX_Events_EventSourceId_Time_Desc_AltKey_Desc" ON "Events" USING btree
(
    "EventSourceId" ASC,
    "Time" DESC NULLS LAST,
    "AltKey" DESC NULLS LAST
);

Algumas informações adicionais possivelmente relevantes:

SELECT version(); --PostgreSQL 13.12, compiled by Visual C++ build 1914, 64-bit
SELECT COUNT(*) FROM "EventSources"; --29,000ish
SELECT COUNT(*) FROM "Events"; --20,000,000ish
SELECT COUNT(*) FROM (SELECT DISTINCT "EventSourceId" FROM "Events") sub; --5,000ish. Most of the "EventSources" don't have "Events" but are used for other things in the db

A pergunta

Aqui está a consulta que estou tentando otimizar:

SELECT
    *
FROM
    (
        SELECT
            "Events".*,
            ROW_NUMBER() OVER (
                PARTITION BY "Events"."EventSourceId"
                ORDER BY
                    "Events"."Time" DESC NULLS LAST,
                    "Events"."AltKey" DESC NULLS LAST
            ) as rankRecent
        FROM
            "Events"
        --WHERE "Events"."Time" < @LimitTime
    ) sub
WHERE
    rankRecent <= @N; -- @N is in the range 1 to 100.

Casos de uso

Aqui estão os casos de uso para a consulta:

  1. Estou carregando um painel que exibe cálculos agregados de dados recentes para cada fonte de evento e, com base nesses cálculos, escolhe quais fontes de eventos serão exibidas e em qual ordem.
  2. Estou investigando um problema que aconteceu ontem às 3h14min15s da manhã e, para uma determinada coleção de fontes de eventos relevantes, preciso ver os 100 eventos que levaram a esse horário para poder ver o que pode ter acontecido. estava dando errado. Neste caso, a WHEREcláusula comentada na consulta será descomentada, podendo também se unir a outra tabela para filtrar fontes de eventos relacionadas a um determinado contexto. Não tenho ideia de como indexar isso, mas esse não é o cerne da questão.

Explicar

Aqui está o resultado de EXPLAIN (ANALYZE, BUFFERS, SETTINGS)(neste caso, @Nfoi definido como 5):

  • Verificação de subconsulta em sub (custo = 0,56..2637123,43 linhas = 6664000 largura = 66) (tempo real = 0,156..90245,642 linhas = 22613 loops = 1)
    • Filtro: (sub.rankrecent <= 5)
    • Linhas removidas pelo filtro: 19963368
    • Buffers: hit compartilhado=6738934 lido=13332278
    • -> WindowAgg (custo = 0,56..2387223,43 linhas = 19992000 largura = 66) (tempo real = 0,155..89355,268 linhas = 19985981 loops = 1)
      • Buffers: hit compartilhado=6738934 lido=13332278
        • -> Varredura de índice usando "IX_Events_EventSourceId_Time_Desc_AltKey_Desc" em "Eventos" (custo = 0,56..1987383,43 linhas = 19992000 largura = 58) (tempo real = 0,100..82274,745 linhas = 19985981 loops = 1)
          • Buffers: hit compartilhado=6738934 lido=13332278
  • Tempo de planejamento: 0,111 ms
  • Tempo de execução: 90247,357ms

Abordagens alternativas que considerei

  1. Considerei usar uma visão materializada para fazer o trabalho pesado de antemão. No entanto, a "Events"tabela terá dados inseridos cerca de uma dúzia de vezes por segundo, e cada transação pode conter de zero a algumas dezenas de linhas. Portanto, se a visualização usar o mesmo plano de consulta que a consulta existente, os dados na visualização ficarão totalmente desatualizados antes que um comando para atualizar a visualização possa ser concluído. Então concluí que não fazia sentido essa estratégia.
  2. Também considerei tentar adicionar uma nova sequencecoluna inteira na "Events"tabela e depois filtrar nessa sequência. Mas isso realmente não resolve o problema. Cada transação que insere eventos pode ou não ter eventos para uma determinada fonte de eventos, portanto, os 5 eventos mais recentes para uma determinada fonte de eventos podem incluir dados de, por exemplo, uma transação de 5 segundos atrás e uma transação de uma hora atrás. , e um de ontem e um da semana passada e um do ano passado. Portanto, não há uma maneira fácil de sincronizar isso sequenceentre milhares de fontes de eventos diferentes. Além disso, é possível que uma fonte de eventos envie manualmente dados que foram perdidos em um momento arbitrário no passado, portanto, a classificação "Time"parece estar correta.
  3. Considerei ter uma tabela que monitorasse apenas, digamos, os 50 eventos mais recentes para cada fonte de evento e descartasse qualquer coisa mais antiga. Dessa forma, a consulta poderia prosseguir e classificar a tabela inteira, pois não haveria tantos dados para classificar. No entanto, a consulta não satisfaria mais o Caso de Uso nº 2; e, embora o caso de uso nº 2 não seja o objetivo desta questão, não posso simplesmente removê-lo da consulta sem fornecer um substituto.

Esclarecimento

A row_number over partitionquestão é a questão principal aqui. O material sobre o Caso de Uso nº 2 e não saber como indexá-lo está incluído aqui, caso inspire alguém a realizar um plano, consulta ou esquema melhor; Não estou esperando uma solução para isso, apenas quero incluir qualquer informação que possa ser relevante.

postgresql
  • 1 1 respostas
  • 96 Views

1 respostas

  • Voted
  1. Best Answer
    jjanes
    2023-10-13T05:07:53+08:002023-10-13T05:07:53+08:00

    Uma nova otimização foi introduzida na v15 que permite parar de calcular as classificações depois de encontrar o número necessário de linhas para cada partição. Isso não é mágico, ele ainda precisa ler todas as linhas porque não sabe onde a próxima partição começa, a não ser lendo as linhas até que a chave da partição mude. Mas deveria pelo menos economizar algum tempo. Quando esta otimização está em vigor, ela aparece no plano com uma linha como:

     WindowAgg  (cost=...
       Run Condition: (row_number() OVER (?) <= 100)
    

    Você provavelmente poderia melhorar isso implementando uma varredura de salto de índice. O PostgreSQL não os implementa automaticamente, mas você pode fazê-lo com um CTE recursivo, conforme mostrado no wiki do projeto (embora pareça que alguém tenha exagerado um pouco desde a última vez que o visitei).

    Como você já tem uma tabela de fontes de eventos distintas, você poderia fazer uso dela com uma junção lateral, em vez de usar o CTE, assim: (não vou ficar pulando no teclado com letras maiúsculas e aspas, eu deixe isso para você)

    select t.* from
      eventsources cross join lateral
      (select * from events 
        where eventsourceid=eventsources.id 
        order by "Events"."Time" DESC NULLS LAST, "Events"."AltKey" DESC NULLS LAST 
        LIMIT 100
      ) t
    

    As linhas não utilizadas em EventSources irão desacelerar um pouco, mas provavelmente ainda será muito mais rápido do que você está fazendo atualmente.

    Para sua pergunta relacionada, não sei, não li com atenção. Faça outra pergunta se tiver outra pergunta, depois de verificar que essa abordagem funciona para você.

    • 2

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

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