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 / 187923
Accepted
Hassan Baig
Hassan Baig
Asked: 2017-10-08 05:02:28 +0800 CST2017-10-08 05:02:28 +0800 CST 2017-10-08 05:02:28 +0800 CST

Otimizando o desempenho lento da consulta SELECT simples

  • 772

Eu tenho um aplicativo chamado 'Links' onde 1) os usuários se reúnem em grupos e adicionam outros, 2) publicam conteúdo uns para os outros nos referidos grupos. Os grupos são definidos por uma links_grouptabela no meu banco de dados postgresql 9.6.5, enquanto as respostas que eles postam neles são definidas por uma links_replytabela. No geral, o desempenho do DB é ótimo.

No entanto, uma SELECTconsulta na links_replytabela está aparecendo consistentemente em slow_log. Está demorando mais de 500 ms e é ~ 10 vezes mais lento do que estou experimentando na maioria das outras operações do postgresql.

Eu usei o Django ORM para gerar a consulta. Aqui está a chamada ORM: replies = Reply.objects.select_related('writer__userprofile').filter(which_group=group).order_by('-submitted_on')[:25]. Essencialmente, isso é selecionar as últimas 25 respostas para um determinado objeto de grupo. Também está selecionando associados usere userprofileobjetos.

Aqui está um exemplo do SQL correspondente do meu log lento: LOG: duration: 8476.309 ms statement:

SELECT

    "links_reply"."id",             "links_reply"."text", 
    "links_reply"."which_group_id", "links_reply"."writer_id",
    "links_reply"."submitted_on",   "links_reply"."image",
    "links_reply"."device",         "links_reply"."category", 

    "auth_user"."id",               "auth_user"."username", 

    "links_userprofile"."id",       "links_userprofile"."user_id",
    "links_userprofile"."score",    "links_userprofile"."avatar" 

FROM 

    "links_reply" 
    INNER JOIN "auth_user" 
        ON ("links_reply"."writer_id" = "auth_user"."id") 
    LEFT OUTER JOIN "links_userprofile" 
        ON ("auth_user"."id" = "links_userprofile"."user_id") 
WHERE "links_reply"."which_group_id" = 124479 
ORDER BY "links_reply"."submitted_on" DESC 
LIMIT 25

Veja os resultados da análise explicativa aqui: https://explain.depesz.com/s/G4X A varredura do índice (para trás) parece estar consumindo o tempo todo.

Aqui está a saída de \d links_reply:

Table "public.links_reply"
     Column     |           Type           |                        Modifiers                         
----------------+--------------------------+----------------------------------------------------------
 id             | integer                  | not null default nextval('links_reply_id_seq'::regclass)
 text           | text                     | not null
 which_group_id | integer                  | not null
 writer_id      | integer                  | not null
 submitted_on   | timestamp with time zone | not null
 image          | character varying(100)   | 
 category       | character varying(15)    | not null
 device         | character varying(10)    | default '1'::character varying
Indexes:
    "links_reply_pkey" PRIMARY KEY, btree (id)
    "category_index" btree (category)
    "links_reply_submitted_on" btree (submitted_on)
    "links_reply_which_group_id" btree (which_group_id)
    "links_reply_writer_id" btree (writer_id)
    "text_index" btree (text)
Foreign-key constraints:
    "links_reply_which_group_id_fkey" FOREIGN KEY (which_group_id) REFERENCES links_group(id) DEFERRABLE INITIALLY DEFERRED
    "links_reply_writer_id_fkey" FOREIGN KEY (writer_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
Referenced by:
    TABLE "links_groupseen" CONSTRAINT "links_groupseen_which_reply_id_fkey" FOREIGN KEY (which_reply_id) REFERENCES links_reply(id) DEFERRABLE INITIALLY DEFERRED
    TABLE "links_report" CONSTRAINT "links_report_which_reply_id_fkey" FOREIGN KEY (which_reply_id) REFERENCES links_reply(id) DEFERRABLE INITIALLY DEFERRED

É uma tabela grande (~ 25 milhões de linhas). O hardware em que está operando tem 16 núcleos e 60 GB de memória. Ele compartilha esta máquina com um aplicativo python. Mas tenho monitorado o desempenho do servidor e não vejo gargalos ali.

Existe alguma maneira de melhorar o desempenho desta consulta? Por favor, informe sobre todas as opções (se houver) que tenho aqui.


Observe que essa consulta teve um desempenho excepcionalmente bom até a semana passada . O que mudou desde então? Realizei um pg_dumpe depois pg_restoredo DB (em uma VM separada), e atualizei do Postgresql 9.3.10 para 9.6.5. Também usei um pool de conexões chamado pgbouncerantes, que ainda não configurei na nova VM para a qual migrei. É isso.

Por fim, também notei (pela experiência do usuário) que para todos os objetos de grupo criados até a semana passada, a consulta ainda é executada rapidamente. Mas todos os novos objetos que estão sendo criados agora estão produzindo um log lento. Isso poderia ser algum tipo de problema de indexação, especificamente com o links_reply_submitted_oníndice?


Atualização: As otimizações prescritas realmente mudaram as coisas. Dar uma olhada:

insira a descrição da imagem aqui

postgresql performance
  • 1 1 respostas
  • 12722 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2017-10-09T13:53:00+08:002017-10-09T13:53:00+08:00

    Principais problemas suspeitos (sinopse)

    1. Você precisa executar ANALYZEapós uma atualização de versão principal com o pg_upgrade. As estatísticas da tabela não são copiadas. Possivelmente, ajuste as configurações de autovacuum também.

    2. Um índice de várias colunas (which_group_id, submitted_on DESC)deve servir muito melhor a essa consulta.

    Consulta

    Consulta formatada sem ruído e com aliases de tabela para melhor legibilidade:

    SELECT lr.id, lr.text, lr.which_group_id, lr.writer_id
         , lr.submitted_on, lr.image, lr.device, lr.category
         , au.id, au.username
         , lu.id, lu.user_id, lu.score, lu.avatar
    FROM   links_reply            lr
    JOIN   auth_user              au ON au.id = lr.writer_id
    LEFT   JOIN links_userprofile lu ON lu.user_id = au.id
    WHERE  lr.which_group_id = 119287
    ORDER  BY lr.submitted_on DESC
    LIMIT  25;
    

    Não vejo problemas com a consulta em si.

    Corrupção do índice?

    (Eu não acho.)

    Isso pode ser algum tipo de problema de indexação?

    Se você suspeitar de corrupção, execute REINDEX. O manual sugere:

    Se você suspeitar de corrupção de um índice em uma tabela de usuário, você pode simplesmente reconstruir esse índice ou todos os índices na tabela, usando REINDEX INDEX ou REINDEX TABLE.

    No caso de acesso concorrente: o bloqueio difere de descartar e recriar índices do zero em vários aspectos. O manual:

    REINDEXé semelhante a descartar e recriar o índice em que o conteúdo do índice é reconstruído do zero. No entanto, as considerações de bloqueio são bastante diferentes. REINDEXbloqueia as gravações, mas não as leituras da tabela pai do índice. Ele também faz um bloqueio exclusivo no índice específico que está sendo processado, o que bloqueará as leituras que tentarem usar esse índice. Em contraste, DROP INDEXmomentaneamente realiza um bloqueio exclusivo na tabela pai, bloqueando tanto as gravações quanto as leituras. O subsequente CREATE INDEXbloqueia as gravações, mas não as leituras; como o índice não está lá, nenhuma leitura tentará usá-lo, o que significa que não haverá bloqueio, mas as leituras podem ser forçadas a varreduras sequenciais caras.

    Se isso ainda for um problema para operações simultâneas, considere CREATE INDEX CONCURRENTLYcriar novos índices duplicados e, em seguida, descarte os antigos em uma transação separada.

    Estatísticas da tabela

    No entanto , parece que as estatísticas da tabela são o problema real. Cotação do seu plano de consulta:

    Varredura de índice para trás usando links_reply_submitted_on em links_reply
    (custo=0,44..1.664.030,07 linhas=2.001 largura=50)
    (tempo real=522.811..716.414 linhas=25 voltas=1)
        Filtro: (which_group_id = 119287)
        Linhas removidas pelo filtro: 1721320

    Minha ênfase em negrito. Parece que o Postgres baseia esse plano de consulta em estatísticas enganosas. Ele espera muito mais acertos e provavelmente também subestima a seletividade do predicado which_group_id = 119287. Acaba filtrando 1,7 milhão de linhas. Isso cheira a estatísticas de tabela imprecisas. E há uma explicação provável, também:

    Ao atualizar as versões principais pg_upgrade, não copia as estatísticas existentes para a nova versão do banco de dados. Recomenda-se executar VACUUM ANALYZEou pelo menos ANALYZEdepois pg_upgrade. A ferramenta ainda solicita para lembrá-lo. O manual:

    Como as estatísticas do otimizador não são transferidas pelo pg_upgrade, você será instruído a executar um comando para gerar novamente essas informações no final da atualização. Pode ser necessário definir parâmetros de conexão para corresponder ao seu novo cluster.

    Se você não fizer isso, as tabelas ficarão sem estatísticas atuais até que o autovacuum seja acionado por gravações suficientes na tabela (ou algum outro comando utilitário como CREATE INDEXou ALTER TABLEatualizar algumas estatísticas em tempo real).

    O mesmo vale para qualquer ciclo de despejo/restauração (com pg_dump& pg_restoreno seu caso). As estatísticas da tabela não são incluídas no dump.

    Sua mesa é muito grande (~25M rows). A configuração padrão para autovacuum define o limite como porcentagem do row_count mais o deslocamento fixo. Às vezes, isso não funciona bem para tabelas grandes, levará algum tempo até a próxima análise automática.

    Execute ANALYZEo manual na mesa ou em todo o banco de dados.

    Relacionado:

    • Tiny table causa degradação extrema do desempenho, corrigida por VACUUM forçado. Por quê?
    • Uso de índice em uma tabela temporária

    Melhor índice

    ... questão de indexação, especificamente com o links_reply_submitted_oníndice?

    Sim, isso também. O índice "links_reply_submitted_on" btree (submitted_on)não está otimizado para o padrão em sua consulta:

    SELECT ...
    FROM   links_reply            lr
    JOIN   ...
    WHERE  lr.which_group_id = 119287
    ORDER  BY lr.submitted_on DESC
    LIMIT  25
    

    Como vimos no plano de consulta acima, o Postgres usa uma varredura de índice, lendo o índice de baixo para cima e filtra as não correspondências. Essa abordagem pode ser razoavelmente rápida se todos (poucos!) selecionados which_group_idtiverem 25 linhas no passado recente. Mas não funcionará tão bem para uma distribuição desigual de linhas ou muitos valores distintos para which_group_id.

    Este índice de várias colunas é um ajuste melhor:

    links_reply__which_group_id__submitted_on btree (which_group_id, submitted_on DESC)
    

    Agora, o Postgres pode apenas escolher as 25 primeiras linhas para o selecionado which_group_id, independentemente da distribuição de dados.

    Relacionado:

    • Índice e desempenho de várias colunas

    Mais explicações

    Sobre sua observação:

    Por fim, também notei (pela experiência do usuário) que para todos os objetos de grupo criados até a semana passada, a consulta ainda é executada rapidamente. Mas todos os novos objetos que estão sendo criados agora estão produzindo um log lento.

    Por quê? Novos objetos podem ainda não ter 25 entradas, então o Postgres precisa continuar varrendo todo o grande índice na esperança de encontrar mais. Embora isso seja extremamente caro com seu antigo índice e plano de consulta, o mesmo é muito barato com o novo índice (e estatísticas de tabela atualizadas).

    Além disso, com estatísticas de tabela precisas, o Postgres provavelmente teria usado seu outro índice "links_reply_which_group_id" btree (which_group_id)para pegar as poucas linhas existentes rapidamente (e classificar se houver mais de 25). Mas meu novo índice oferece um plano de consulta mais confiável em qualquer caso.

    Coisas menores

    Existem várias outras coisas (menores) que você pode fazer, como otimizar o layout da mesa ou ajustar as configurações de autovacuum, mas essa resposta já é longa o suficiente. Relacionado:

    • Configurando o PostgreSQL para desempenho de leitura

    E mais tarde você comentou:

    Também removeu campos estranhos ...

    Certamente ajuda a recuperar apenas as colunas que você realmente precisa. Faça isso, além disso. Mas não é a questão principal aqui.

    • 9

relate perguntas

  • Sequências Biológicas do UniProt no PostgreSQL

  • Como determinar se um Índice é necessário ou necessário

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • 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