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 / 151169
Accepted
ARX
ARX
Asked: 2016-10-01 10:05:39 +0800 CST2016-10-01 10:05:39 +0800 CST 2016-10-01 10:05:39 +0800 CST

As visualizações são prejudiciais para o desempenho no PostgreSQL?

  • 772

O seguinte é um trecho de um livro sobre design de banco de dados (Beginning Database Design ISBN: 0-7645-7490-6):

O perigo de usar visualizações é filtrar uma consulta em uma visualização, esperando ler uma parte muito pequena de uma tabela muito grande. Qualquer filtragem deve ser feita na exibição porque qualquer filtragem na própria exibição é aplicada depois que a consulta na exibição conclui a execução. As exibições geralmente são úteis para acelerar o processo de desenvolvimento, mas, a longo prazo, podem matar completamente o desempenho do banco de dados.

O seguinte é um trecho da documentação do PostgreSQL 9.5:

Fazer uso liberal de visualizações é um aspecto fundamental do bom design de banco de dados SQL. As visualizações permitem encapsular os detalhes da estrutura de suas tabelas, que podem mudar à medida que seu aplicativo evolui, por trás de interfaces consistentes.

As duas fontes parecem se contradizer ("não projete com vistas" vs. "faça design com vistas").

No entanto, nas visualizações PG são implementadas usando o sistema de regras. Portanto, possivelmente (e esta é a minha pergunta) qualquer filtragem na exibição é reescrita como um filtro na exibição, resultando em uma única execução de consulta nas tabelas subjacentes.

Minha interpretação está correta e o PG combina cláusulas WHERE dentro e fora da visão? Ou ele os executa separadamente, um após o outro? Algum exemplo curto, autocontido, correto (compilável)?

postgresql optimization
  • 4 4 respostas
  • 66349 Views

4 respostas

  • Voted
  1. Best Answer
    a_horse_with_no_name
    2016-10-02T06:11:08+08:002016-10-02T06:11:08+08:00

    O livro está errado.

    A seleção de uma exibição é exatamente tão rápida ou lenta quanto a execução da instrução SQL subjacente – você pode verificar isso facilmente usando explain analyze.

    O otimizador do Postgres (e o otimizador para muitos outros DBMSs modernos) será capaz de empurrar predicados na visão para a instrução de visão real – desde que esta seja uma instrução simples (novamente, isso pode ser verificado usando explain analyze).

    A "má reputação" em relação ao desempenho decorre – eu acho – de quando você usa visualizações em excesso e começa a construir visualizações que usam visualizações que usam visualizações. Muitas vezes isso resulta em declarações que fazem muito em comparação com uma declaração que foi feita sob medida sem as visualizações, por exemplo, porque algumas tabelas intermediárias não seriam necessárias. Em quase todos os casos, o otimizador não é inteligente o suficiente para remover essas tabelas/junções desnecessárias ou para empurrar predicados em vários níveis de visualizações (isso também é verdade para outros SGBDs).

    • 96
  2. Erwin Brandstetter
    2016-10-03T18:36:54+08:002016-10-03T18:36:54+08:00

    Para dar um exemplo do que @a_horse explicou :

    O Postgres implementa o esquema de informações, que consiste em visualizações (às vezes complexas) que fornecem informações sobre objetos de banco de dados de forma padronizada. Isso é conveniente e confiável - e pode ser substancialmente mais caro do que acessar as tabelas do catálogo Postgres diretamente.

    Exemplo muito simples, para obter todas as colunas visíveis de uma tabela
    ... do esquema de informações:

    SELECT column_name
    FROM   information_schema.columns
    WHERE  table_name = 'big'
    AND    table_schema = 'public';
    

    ... do catálogo do sistema:

    SELECT attname
    FROM   pg_catalog.pg_attribute
    WHERE  attrelid = 'public.big'::regclass
    AND    attnum > 0
    AND    NOT attisdropped;
    

    Compare os planos de consulta e o tempo de execução para ambos com o EXPLAIN ANALYZE.

    • A primeira consulta é baseada na view information_schema.columns, que une várias tabelas que não precisamos para isso.

    • A segunda consulta verifica apenas uma tabela pg_catalog.pg_attribute, portanto, muito mais rápido. (Mas a primeira consulta ainda precisa de apenas alguns ms em bancos de dados comuns.)

    Detalhes:

    • Consulta para retornar nomes de colunas de saída e tipos de dados de uma consulta, tabela ou exibição
    • 25
  3. enjayaitch
    2018-08-11T11:37:49+08:002018-08-11T11:37:49+08:00

    EDITAR:

    Com desculpas, preciso retirar minha afirmação de que a resposta aceita nem sempre está correta - ela afirma que a visão é sempre idêntica à mesma coisa escrita como uma subconsulta. Acho que isso é indiscutível, e acho que agora sei o que está acontecendo no meu caso.

    Agora também acho que há uma resposta melhor para a pergunta original.

    A questão original é sobre se deve ser a prática de orientação para usar visualizações (em oposição a, por exemplo, repetir SQL em rotinas que podem precisar ser mantidas duas ou mais vezes).

    Minha resposta seria "não se sua consulta usar funções de janela ou qualquer outra coisa que faça com que o otimizador trate a consulta de maneira diferente quando ela se tornar uma subconsulta, porque o próprio ato de criar a subconsulta (representada como uma visualização ou não) pode prejudicar o desempenho se você estiver filtrando com parâmetros em tempo de execução.

    A complexidade da minha função de janela é desnecessária. O plano de explicação para isso:

    SELECT DISTINCT ts.train_service_key,
                pc.assembly_key,
                count(*) OVER 
                  (PARTITION BY ts.train_service_key) AS train_records
    FROM staging.train_service ts
       JOIN staging.portion_consist pc 
         USING (ds_code, train_service_key)
    WHERE assembly_key = '185132';
    

    é muito menos dispendioso do que para isso:

    SELECT *
    FROM (SELECT DISTINCT ts.train_service_key,
                pc.assembly_key,
                count(*) OVER
                  (PARTITION BY ts.train_service_key) AS train_records
    FROM staging.train_service ts
       JOIN staging.portion_consist pc
         USING (ds_code, train_service_key)) AS query
    WHERE assembly_key = '185132';
    

    Espero que seja um pouco mais específico e útil.

    Na minha experiência recente (fazendo com que eu encontre essa pergunta), a resposta aceita acima não está correta em todas as circunstâncias. Eu tenho uma consulta relativamente simples que inclui uma função de janela:

    SELECT DISTINCT ts.train_service_key,
                    pc.assembly_key,
                    dense_rank() OVER (PARTITION BY ts.train_service_key
                    ORDER BY pc.through_idx DESC, pc.first_portion ASC,
                   ((CASE WHEN (NOT ts.primary_direction)
                     THEN '-1' :: INTEGER
                     ELSE 1
                     END) * pc.first_seq)) AS coach_block_idx
    FROM (staging.train_service ts
    JOIN staging.portion_consist pc USING (ds_code, train_service_key))
    

    Se eu adicionar este filtro:

    where assembly_key = '185132'
    

    O plano de explicação que recebo é o seguinte:

    QUERY PLAN
    Unique  (cost=11562.66..11568.77 rows=814 width=43)
      ->  Sort  (cost=11562.66..11564.70 rows=814 width=43)
        Sort Key: ts.train_service_key, (dense_rank() OVER (?))
        ->  WindowAgg  (cost=11500.92..11523.31 rows=814 width=43)
              ->  Sort  (cost=11500.92..11502.96 rows=814 width=35)
                    Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                    ->  Nested Loop  (cost=20.39..11461.57 rows=814 width=35)
                          ->  Bitmap Heap Scan on portion_consist pc  (cost=19.97..3370.39 rows=973 width=38)
                                Recheck Cond: (assembly_key = '185132'::text)
                                ->  Bitmap Index Scan on portion_consist_assembly_key_index  (cost=0.00..19.72 rows=973 width=0)
                                      Index Cond: (assembly_key = '185132'::text)
                          ->  Index Scan using train_service_pk on train_service ts  (cost=0.43..8.30 rows=1 width=21)
                                Index Cond: ((ds_code = pc.ds_code) AND (train_service_key = pc.train_service_key))
    

    Isso está usando o índice de chave primária na tabela de serviço de trem e um índice não exclusivo na tabela partition_consist. Executa em 90ms.

    Eu criei uma visão (colando aqui para ficar absolutamente claro, mas é literalmente a consulta em uma visão):

    CREATE OR REPLACE VIEW staging.v_unit_coach_block AS
    SELECT DISTINCT ts.train_service_key,
                pc.assembly_key,
                dense_rank() OVER (PARTITION BY ts.train_service_key
                  ORDER BY pc.through_idx DESC, pc.first_portion ASC, (
                    (CASE
                  WHEN (NOT ts.primary_direction)
                    THEN '-1' :: INTEGER
                  ELSE 1
                  END) * pc.first_seq)) AS coach_block_idx
     FROM (staging.train_service ts
      JOIN staging.portion_consist pc USING (ds_code, train_service_key))
    

    Quando eu consulto essa visualização com o filtro idêntico:

    select * from staging.v_unit_coach_block
    where assembly_key = '185132';
    

    Este é o plano de explicação:

    QUERY PLAN
    Subquery Scan on v_unit_coach_block  (cost=494217.13..508955.10     rows=3275 width=31)
    Filter: (v_unit_coach_block.assembly_key = '185132'::text)
     ->  Unique  (cost=494217.13..500767.34 rows=655021 width=43)
        ->  Sort  (cost=494217.13..495854.68 rows=655021 width=43)
              Sort Key: ts.train_service_key, pc.assembly_key, (dense_rank() OVER (?))
              ->  WindowAgg  (cost=392772.16..410785.23 rows=655021 width=43)
                    ->  Sort  (cost=392772.16..394409.71 rows=655021 width=35)
                          Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
                          ->  Hash Join  (cost=89947.40..311580.26 rows=655021 width=35)
                                Hash Cond: ((pc.ds_code = ts.ds_code) AND (pc.train_service_key = ts.train_service_key))
                                ->  Seq Scan on portion_consist pc  (cost=0.00..39867.86 rows=782786 width=38)
                                ->  Hash  (cost=65935.36..65935.36 rows=1151136 width=21)
                                      ->  Seq Scan on train_service ts  (cost=0.00..65935.36 rows=1151136 width=21)
    

    Isso está fazendo varreduras completas em ambas as tabelas e leva 17s.

    Até me deparar com isso, usei liberalmente as visualizações com o PostgreSQL (tendo entendido as visões amplamente aceitas expressas na resposta aceita). Eu evitaria especificamente o uso de visualizações se precisar de filtragem pré-agregada, para a qual usaria funções de retorno de conjunto.

    Também estou ciente de que os CTEs no PostgreSQL são estritamente avaliados separadamente, por design, então não os uso da mesma maneira que faria com o SQL Server, por exemplo, onde eles parecem ser otimizados como subconsultas.

    Minha resposta, portanto, é que há casos em que as visualizações não funcionam exatamente como a consulta na qual se baseiam, portanto, recomenda-se cautela. Estou usando o Amazon Aurora baseado no PostgreSQL 9.6.6.

    • 12
  4. Andreas Covidiot
    2018-01-10T02:33:21+08:002018-01-10T02:33:21+08:00

    (Sou um grande fã de visualizações, mas você precisa ter muito cuidado com o PG aqui e gostaria de incentivar todos a usar visualizações geralmente também no PG para melhor compreensão e manutenção de consultas/código)

    Na verdade e infelizmente (AVISO:) usar visualizações no Postgres nos causou problemas reais e diminuiu muito nosso desempenho dependendo dos recursos que estávamos usando dentro dele :-( (pelo menos com v10.1 / upd: em relação à v12 veja abaixo). ( Isso não seria assim com outros sistemas de banco de dados modernos como o Oracle.)

    Então, possivelmente (e esta é a minha pergunta) qualquer filtragem contra a visão ... resultando em uma única execução de consulta nas tabelas subjacentes.

    (Dependendo do que você quer dizer exatamente - não - tabelas temporárias intermediárias podem ser materializadas que você pode não querer ou onde os predicados não são pressionados ...)

    Conheço pelo menos dois grandes "recursos", que nos decepcionaram no meio das migrações do Oracle para o Postgres , então tivemos que abandonar o PG em um projeto:

    • CTEs ( -clausewith subqueries / common table expression ) são (geralmente) úteis para estruturar consultas mais complexas (mesmo em aplicativos menores), mas no PG são implementados por design como dicas de otimizador "ocultas" (gerando, por exemplo, tabelas temporárias não indexadas) e violando assim o (para mim e muitos outros importantes) conceito de SQL declarativo ( Oracle docu ) ( upd 2020-08-02 : com PG v12 + esses planos de consulta de amostra devem ser os mesmos agora): por exemplo

      • consulta simples:

            explain
        
              select * from pg_indexes where indexname='pg_am_name_index'
        
            /* result: 
        
            Nested Loop Left Join  (cost=12.38..26.67 rows=1 width=260)
              ...
              ->  Bitmap Index Scan on pg_class_relname_nsp_index  (cost=0.00..4.29 rows=2 width=0)
                                                     Index Cond: (relname = 'pg_am_name_index'::name)
              ...
            */
        
      • reescrito usando algum CTE:

            explain
        
              with 
        
              unfiltered as (
                select * from pg_indexes
              ) 
        
              select * from unfiltered where indexname='pg_am_name_index'
        
            /* result:
        
            CTE Scan on unfiltered  (cost=584.45..587.60 rows=1 width=288)
               Filter: (indexname = 'pg_am_name_index'::name)
               CTE unfiltered
                 ->  Hash Left Join  (cost=230.08..584.45 rows=140 width=260)  
            ...
            */
        
      • outras fontes com discussões etc.: https://blog.2ndquadrant.com/postgresql-ctes-are-optimization-fences/

    • funções de janela com over-instruções são potencialmente inutilizáveis ​​(normalmente usadas em visualizações, por exemplo, como fonte de relatórios baseados em consultas mais complexas)


    with-cláusulas podem funcionar desde a v12

    atualização 2020-08-01 : olhando para os documentos v12 with em alguns ou todos os seus casos , há uma otimização necessária há muito tempo (mas ainda provavelmente não está funcionando se as funções da janela forem usadas lá, conforme detalhado logo acima):

    No entanto, se uma consulta WITH não for recursiva e sem efeitos colaterais (ou seja, for um SELECT que não contém funções voláteis) , ela poderá ser dobrada na consulta pai, permitindo a otimização conjunta dos dois níveis de consulta. Por padrão , isso acontece se a consulta pai fizer referência à consulta WITH apenas uma vez , mas não se fizer referência à consulta WITH mais de uma vez. Você pode substituir essa decisão especificando MATERIALIZED para forçar o cálculo separado da consulta WITH ou especificando NOT MATERIALIZEDpara forçá-lo a ser mesclado na consulta pai. A última opção arrisca a computação duplicada da consulta WITH, mas ainda pode gerar uma economia líquida se cada uso da consulta WITH precisar apenas de uma pequena parte da saída completa da consulta WITH.

    nossa solução alternativa para as withcláusulas (PG v10)

    Vamos transformar todas as "visualizações inline" em visualizações reais com um prefixo especial para que elas não atrapalhem a lista/namespace de visualizações e possam ser facilmente relacionadas à "visualização externa" original :-/


    nossa solução para as funções da janela

    Implementamos com sucesso usando o banco de dados Oracle.

    • 3

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