PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL guts:O que é “resjunk”?


Estou me aprofundando no analisador PostgreSQL, no reescritor de consultas e no planejador de consultas no momento, como parte do trabalho de segurança em nível de linha para o projeto AXLE. Como notei que há uma ótima documentação sobre a estrutura geral e o fluxo, mas não muito sobre alguns detalhes, pensei em começar a postar sobre alguns dos cantos mais confusos.

Se você não está interessado no código-fonte do PostgreSQL e em seu funcionamento interno, pode parar de ler agora.

ressaca


O tópico de hoje é o termo “resjunk”, que se refere a resjunk atributo de lista de destino. Você verá esse termo em todo o planejador e reescritor, geralmente como conhecimento presumido. O nome não é extremamente útil.

ressaca colunas são descritas em src/backend/executor/execJunk.c , onde há um comentário moderadamente detalhado. Isso realmente não explica as ideias abrangentes, no entanto.

O conceito é que às vezes o PostgreSQL precisa acompanhar as informações por tupla que não fazem parte da saída da consulta. Pode ser uma chave de classificação que não faz parte da lista de seleção, um resultado intermediário de uma subconsulta que é usada como filtro e descartada ou pode ser uma coluna interna como ctid que não é exposto aos usuários.

Os nós do plano têm listas de destino – são listas das colunas de saída desse nó do plano. Para um simples teste SELECT a, b FROM as colunas a e b aparecerá na lista de destino do nó do plano índice ou seqscan para a consulta. Você mesmo pode observar isso habilitando o log do plano, de acordo com a seguinte saída aparada:
regress=> CREATE TABLE 
regress=> SET enable_print_plan = on;
regress=> SET client_min_messages = debug;
regress=> SELECT a, b FROM test;
LOG:  plan:
DETAIL:     {PLANNEDSTMT 
   :commandType 1 
   :queryId 0 
   ....
   :planTree 
      {SEQSCAN 
      :startup_cost 0.00 
      :total_cost 29.40 
      :plan_rows 1940 
      :plan_width 12 
      :targetlist (
         {TARGETENTRY 
         :expr 
            {VAR 
            :varno 1 
            :varattno 1 
            ...
            :location 7
            }
         ...
         :resjunk false
         }
         {TARGETENTRY 
         :expr 
            {VAR 
            :varno 1 
            :varattno 2 
            ...
            :location 10
            }
         ....
         :resjunk false
         }
      )
      :qual  
      :lefttree  
      :righttree  
      :initPlan  
      :extParam (b)
      :allParam (b)
      :scanrelid 1
      }
   :rtable (
      {RTE 
      :alias  
      :eref 
         {ALIAS 
         :aliasname test 
         :colnames ("a" "b")
         }
      ...
      :selectedCols (b 9 10)
      :modifiedCols (b)
      }
   )
   ....
   }

Esse é o plano detalhado para:
                       QUERY PLAN                       
--------------------------------------------------------
 Seq Scan on test  (cost=0.00..29.40 rows=1940 width=8)

Nele você verá que o SELECT tem duas entradas na lista de destino, uma para cada coluna. Nem é resjunk uma vez que ambos são gerados pela consulta.

E se adicionarmos uma classificação por coluna c , que não está no SELECT -list, veremos uma nova coluna adicionada à lista de destino e marcada como resjunk:
regress=> SELECT a, b FROM test ORDER BY c;
LOG:  plan:
DETAIL:     {PLANNEDSTMT 
   :commandType 1 
   ....
   :planTree 
      {SORT 
      ....
      :targetlist (
         {TARGETENTRY 
         :expr 
            {VAR 
            :varno 65001 
            :varattno 1
            ...
            }
         :resno 1 
         :resname a 
         ...
         :resjunk false
         }
         {TARGETENTRY 
         :expr 
            {VAR 
            :varno 65001 
            :varattno 2 
            ...
            }
         :resno 2 
         :resname b 
         ....
         :resjunk false
         }
         {TARGETENTRY 
         :expr 
            {VAR 
            :varno 65001 
            :varattno 3 
            ...
            }
         :resno 3 
         :resname  
         ....
         :resjunk true
         }
      )
      :qual  
      :lefttree 
         {SEQSCAN 
         ...
         :targetlist (
            {TARGETENTRY 
            :expr 
               {VAR 
               :varno 1 
               :varattno 1 
               ...
               }
            :resno 1 
            ...
            :resjunk false
            }
            {TARGETENTRY 
            :expr 
               {VAR 
               :varno 1 
               :varattno 2 
               ...
               }
            :resno 2 
            ...
            :resjunk false
            }
            {TARGETENTRY 
            :expr 
               {VAR 
               :varno 1 
               :varattno 3 
               ...
               }
            :resno 3
            ...
            :resjunk true
            }
         )
         ....
      }
   :rtable (
      {RTE 
      :alias  
      :eref 
         {ALIAS 
         :aliasname test 
         :colnames ("a" "b" "c")
         }
      ....
      :selectedCols (b 9 10 11)
      :modifiedCols (b)
      }
   )
   ....
   }

para o plano de consulta:
                          QUERY PLAN                           
---------------------------------------------------------------
 Sort  (cost=135.34..140.19 rows=1940 width=12)
   Sort Key: c
   ->  Seq Scan on test  (cost=0.00..29.40 rows=1940 width=12)
(3 rows)

Então c está marcado como resíduo porque é uma chave de classificação que não faz parte da saída final do plano.

Você também verá ctid marcado rejunk em ATUALIZAR e EXCLUIR planos por motivos semelhantes – a parte lida do plano busca as linhas a serem modificadas e seus IDs de tupla; estes são puxados para um MODIFYTABLE mais externo plan node que atualiza a linha para marcá-la como excluída e, se for uma atualização, insere uma nova versão da linha.

A pesquisa que levou a esses resultados recebeu financiamento do Sétimo Programa-Quadro da União Europeia (FP7/2007-2013) sob o contrato de doação n° 318633