Como cada processo trabalhador executa a parte paralela do plano até a conclusão, não é possível simplesmente pegar um plano de consulta comum e executá-lo usando vários processos trabalhadores. Cada processo trabalhador produziria uma cópia completa do conjunto de resultados de saída, portanto, a consulta não seria executada mais rápido que o normal, mas produziria resultados incorretos. Em vez disso, a parte paralela do plano deve ser o que é conhecido internamente pelo otimizador de consulta como plano parcial; ou seja, deve ser construído de modo que cada processo que executa o plano gere apenas um subconjunto das linhas de saída, de modo que cada linha de saída necessária seja gerada por exatamente um dos processos cooperativos. Geralmente, isto significa que a varredura na tabela de direcionamento da consulta deve ser uma varredura com reconhecimento de paralelismo.
Os seguintes tipos de varreduras de tabela com reconhecimento de paralelismo têm suporte no momento.
Em uma varredura sequencial paralela, os blocos da tabela serão divididos em intervalos e compartilhados entre os processos cooperantes. Cada processo trabalhador concluirá a varredura do seu intervalo de blocos antes de solicitar um intervalo adicional.
Em uma varredura de heap (pilha) de bitmap paralela, um processo é escolhido como líder. Este processo executa a varredura de um ou mais índices, e cria um bitmap indicando quais blocos de tabela precisam ser acessados. Estes blocos são então divididos entre os processos cooperantes, como em uma varredura sequencial paralela. Em outras palavras, a varredura do heap é executada em paralelo, mas a varredura do índice subjacente não.
Em uma varredura de índice paralela ou varredura somente de índice paralela, os processos cooperantes se revezam na leitura de dados do índice. No momento, as varreduras de índice paralelas são possíveis apenas para índices B-Tree. Cada processo reivindicará um único bloco de índice e examinará e retornará todas as tuplas referenciadas por este bloco; outros processos podem, ao mesmo tempo, retornar tuplas de um bloco de índice diferente. Os resultados da varredura paralela no índice B-Tree são retornados de forma ordenada dentro de cada processo trabalhador.
Outros tipos de varreduras, como as varreduras de índices não B-Tree, poderão oferecer suporte a varreduras paralelas no futuro.
Assim como em um plano sem paralelismo, a tabela de controle pode ser unida a uma ou mais outras tabelas usando um laço aninhado, junção de hash ou junção de mesclagem. O lado interno da junção pode ser qualquer tipo de plano sem paralelismo com suporte pelo planejador, desde que seja seguro executar em um processo trabalhador paralelo. Dependendo do tipo de junção, o lado interno também pode ser um plano com paralelismo.
Em uma junção de laço aninhado, o lado interno é sempre não paralelo. Embora seja executado na íntegra, isto será eficiente se o lado interno for uma varredura de índice, porque as tuplas externas e, portanto, os laços que procuram valores no índice, serão divididos pelos processos cooperantes.
Em uma junção de mesclagem, o lado interno é sempre um plano sem paralelismo, portanto, executado integralmente. Isto pode ser ineficiente, principalmente se deverá ser realizada uma ordenação, porque o trabalho e os dados resultantes serão duplicados em cada processo cooperante.
Em uma junção de hash (sem o prefixo “paralelo”), o lado interno é executado integralmente por todos os processos cooperantes para construir cópias idênticas da tabela de hash. Isto poderá ser ineficiente se a tabela de hash for grande, ou o plano for caro. Em uma junção de hash paralela, o lado interno é um hash paralelo que divide o trabalho de construir a tabela de hash compartilhada entre os processos cooperantes.
O PostgreSQL oferece suporte a agregação paralela agregando em dois estágios. Primeiro, cada processo que participa da parte paralela da consulta executa uma etapa de agregação, produzindo um resultado parcial para cada grupo do qual este processo está ciente. Isto é refletido no plano como um nó Partial Aggregate. Em segundo lugar, os resultados parciais são transferidos para o líder via Gather ou Gather Merge. Finalmente, o líder reagrega os resultados de todos os processos trabalhadores para produzir o resultado final. Isto é refletido no plano como um nó Finalize Aggregate.
Como o nó Finalize Aggregate executa no processo líder, as consultas que produzem um número relativamente grande de grupos em comparação com o número de linhas de entrada parecerão menos favoráveis ao planejador de consultas. Por exemplo, no pior cenário, o número de grupos vistos pelo nó Finalize Aggregate pode ser igual ao número de linhas de entrada que foram vistas por todos os processos de trabalho no estágio Partial Aggregate. Para estes casos, claramente não haverá benefício de desempenho no uso da agregação paralela. O planejador de consulta leva isto em consideração durante o processo de planejamento e, provavelmente, não escolherá a agregação paralela neste cenário.
A agregação paralela não tem suporte em todas as situações.
Cada agregação deve ser segura
para paralelismo, e deve ter uma função de combinação.
Se a agregação tiver um estado de transição do tipo
internal, deverá ter funções de serialização
e desserialização.
Veja CREATE AGGREGATE para obter mais detalhes.
A agregação paralela não terá suporte se qualquer chamada de função
de agregação contiver a cláusula DISTINCT ou
ORDER BY e, também, não tem suporte para
agregações de conjuntos ordenados, ou quando a consulta envolve
GROUPING SETS.
A agregação paralela só pode ser usada quando todas as junções
envolvidas na consulta também fazem parte da parte paralela do plano.
Sempre que o PostgreSQL precisa combinar
linhas de várias fontes em um único conjunto de resultados, ele usa
um nó de plano Append ou
MergeAppend.
Isto geralmente acontece ao implementar UNION ALL,
ou ao varrer uma tabela particionada.
Estes nós podem ser usados em planos com paralelismo, assim como em
qualquer outro plano.
Entretanto, em um plano com paralelismo o planejador pode usar um nó
Parallel Append.
Quando é usado um nó Append em um plano com paralelismo, cada processo executa os planos filho na ordem em que aparecem, de modo que todos os processos participantes cooperem para executar o primeiro plano filho até que seja concluído e, então, passar para o segundo plano mais ou menos ao mesmo tempo. Quando é usado um nó Parallel Append, o executor distribui os processos participantes da maneira mais uniforme possível entre seus planos filho, para que vários planos filho sejam executados simultaneamente, evitando a contenção e, também, evitando pagar o custo de inicialização de um plano filho para processos que nunca o executam.
Também, ao contrário de um nó Append
regular, que só pode ter filhos parciais quando usado em um
plano com paralelismo, um nó
Parallel Append pode ter
planos filhos parciais e não parciais.
Filhos não parciais serão varridos por apenas um único processo,
já que os varrer mais de uma vez produziria resultados duplicados.
Os planos que envolvem o acréscimo de vários conjuntos de resultado
podem, portanto, atingir um paralelismo de granularidade grosseira,
mesmo quando planos parciais eficientes não estão disponíveis.
Por exemplo, considere uma consulta em uma tabela particionada que
só pode ser implementada com eficiência usando um índice que não
oferece suporte a verificações paralelas.
O planejador pode escolher um
Parallel Append de planos regulares
Index Scan; cada varredura de índice individual
teria que ser executada até a conclusão por um único processo,
mas varreduras diferentes poderiam ser executadas ao mesmo tempo
por processos diferentes.
Pode ser usada a variável de configuração enable_parallel_append para desativar este recurso.
Se uma consulta que deve produzir um plano com paralelismo não produzir um plano com paralelismo, pode-se tentar reduzir a variável de configuração parallel_setup_cost ou parallel_tuple_cost. É claro que este plano pode ser mais lento do que o plano serial que o planejador preferiu, mas nem sempre será este o caso. Se não for obtido um plano com paralelismo, mesmo com valores muito pequenos destas configurações (por exemplo, após defini-las como zero), poderá haver algum motivo pelo qual o planejador de consultas não consiga gerar um plano com paralelismo para a consulta. Veja Seção 15.2 e a Seção 15.4 para obter informações sobre por que isso pode ser o caso.
Ao executar um plano com paralelismo, pode-se usar
EXPLAIN (ANALYZE, VERBOSE) para mostrar
estatísticas por processo trabalhador para cada nó do plano.
Isto pode ser útil para determinar se o trabalho está sendo
distribuído uniformemente entre todos os nós do plano e,
de maneira mais geral, para entender as características de
desempenho do plano.