Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Conforme discutido nas seções anteriores, o Citus é uma extensão que estende o PostgreSQL mais recente para execução distribuída. Com o Citus, você pode usar consultas SELECT do PostgreSQL padrão no coordenador do Citus para consulta. Em seguida, o Citus paraleliza as consultas SELECT que envolvem seleções complexas, agrupamentos e ordenações e JOINs para acelerar o desempenho da consulta. Em um alto nível, o Citus particiona a consulta SELECT em fragmentos de consulta menores, atribui os fragmentos de consulta aos trabalhadores, supervisiona sua execução, mescla seus resultados (e os ordena, se necessário), e retorna o resultado final para o usuário.
Nas seções a seguir, você aprenderá sobre os diferentes tipos de consultas que podem ser executadas usando o Citus.
Funções de agregação
O Citus dá suporte e paraleliza a maioria das funções de agregação com suporte pelo PostgreSQL, incluindo agregações personalizadas definidas pelo usuário. As agregações são executadas usando um dos três métodos, nesta ordem de preferência:
Quando a agregação é agrupada pela coluna de distribuição de uma tabela, o Citus pode reduzir a execução de toda a consulta para cada trabalho. Todas as agregações têm suporte nessa situação e são executadas em paralelo nos nós de trabalho. (O TYou deve instalar agregações personalizadas, que você planeja usar, nos nós de trabalho.)
Quando a agregação não é agrupada pela coluna de distribuição de uma tabela, o Citus ainda pode otimizar caso a caso. O Citus tem regras internas para determinadas agregações, como
sum(),avg()ecount(distinct)que permitem reescrever consultas para agregação parcial em trabalhadores. Por exemplo, para calcular uma média, o Citus obtém uma soma e uma contagem de cada trabalho e, em seguida, o nó coordenador calcula a média final.Aqui está a lista completa das agregações de casos especiais:
avg, min, max, sum, count, array_agg, jsonb_agg, jsonb_object_agg, json_agg, json_object_agg, bit_and, bit_or, bool_and, bool_or, every, hll_add_agg, hll_union_agg, topn_add_agg, topn_union_agg, any_value, tdigest(double precision, int), tdigest_percentile(double precision, int, double precision), tdigest_percentile(double precision, int, double precision\[\]), tdigest_percentile(tdigest, double precision), tdigest_percentile(tdigest, double precision\[\]), tdigest_percentile_of(double precision, int, double precision), tdigest_percentile_of(double precision, int, double precision\[\]), tdigest_percentile_of(tdigest, double precision), tdigest_percentile_of(tdigest, double precision\[\])Último recurso: efetuar pull de todas as linhas dos trabalhadores e executar a agregação no nó coordenador. Quando a agregação não é agrupada em uma coluna de distribuição e não é um dos casos especiais predefinidos, o Citus volta a essa abordagem. Isso causa sobrecarga de rede e pode esgotar os recursos do coordenador se o conjunto de dados a ser agregado for muito grande. (Conforme mostrado no exemplo a seguir, você pode desabilitar esse fallback.)
Pequenas alterações em uma consulta podem alterar os modos de execução, causando ineficiência potencialmente surpreendente. Por exemplo,
sum(x)agrupada por uma coluna de não distribuição poderia usar a execução distribuída, enquantosum(distinct x)precisa efetuar pull de todo o conjunto de registros de entrada para o coordenador.Uma coluna pode prejudicar a execução de uma consulta inteira. No exemplo a seguir, se
sum(distinct value2)tiver que ser agrupado no coordenador, o mesmo farásum(value1)mesmo se este último estiver bem por conta própria.SELECT sum(value1), sum(distinct value2) FROM distributed_table;Para evitar a extração acidental de dados para o coordenador, você pode definir uma GUC (configuração de usuário global):
SET citus.coordinator_aggregation_strategy TO 'disabled';Desabilitar a estratégia de agregação do coordenador impede que as consultas agregadas do tipo três funcionem.
Agregações de contagem(distinta)
O Citus dá count(distinct) suporte a agregações de várias maneiras. Se a count(distinct) agregação estiver na coluna de distribuição, o Citus poderá enviar diretamente a consulta para os trabalhos. Caso contrário, o Citus executará selecionar instruções distintas em cada trabalho e retornará a lista ao coordenador em que obtém a contagem final.
A transferência desses dados fica mais lenta quando os trabalhadores têm um número maior de itens distintos. Especialmente para consultas que contêm várias count(distinct) agregações, por exemplo:
-- multiple distinct counts in one query tend to be slow
SELECT count(distinct a), count(distinct b), count(distinct c)
FROM table_abc;
Para esse tipo de consulta, as instruções distintas selecionadas resultantes nos trabalhos produzem essencialmente um produto cruzado de linhas a serem transferidas para o coordenador.
Para aumentar o desempenho, você pode optar por fazer uma contagem aproximada seguindo estas etapas:
Baixe e instale a extensão hll em todas as instâncias do PostgreSQL (o coordenador e todos os trabalhadores).
Visite o repositório GitHub postgresql-hll para obter detalhes sobre como obter a extensão hll que define a estrutura de dados do HLL (HyperLogLog).
Crie a extensão hll em todas as instâncias do PostgreSQL executando o seguinte comando do coordenador:
CREATE EXTENSION hll;Habilite
count_distinctas aproximações definindo o valor decitus.count_distinct_error_rateconfiguração. Espera-se que valores mais baixos para essa configuração forneçam resultados mais precisos, mas levem mais tempo para a computação. Defina esse valor como 0,005.SET citus.count_distinct_error_rate to 0.005;
Após essa etapa, count(distinct) as agregações alternam automaticamente para o uso de HLL, sem nenhuma alteração necessária para suas consultas. Você deve ser capaz de executar consultas distintas de contagem aproximada em qualquer coluna da tabela.
Coluna HyperLogLog
Determinados usuários já armazenam seus dados como colunas HLL. Nesses casos, eles podem acumular dinamicamente esses dados chamando hll_union_agg(hll_column).
Estimando os n itens principais
Você pode calcular os primeiros n elementos em um conjunto aplicando contagem, classificação e limite. No entanto, à medida que os tamanhos dos dados aumentam, esse método se torna lento e com uso intensivo de recursos. É mais eficiente usar uma aproximação.
A extensão TopN de software livre para PostgreSQL permite resultados aproximados rápidos para consultas top-n . A extensão materializa os valores principais em um tipo de dados JSON. O TopN pode atualizar incrementalmente esses valores principais ou mesclá-los sob demanda em intervalos de tempo diferentes.
Operações básicas
Antes de ver um exemplo realista de TopN, vamos ver como algumas de suas operações primitivas funcionam. Primeiro, topn_add atualiza um objeto JSON com contagens de quantas vezes uma chave é vista:
-- starting from nothing, record that we saw an "a"
select topn_add('{}', 'a');
-- => {"a": 1}
-- record the sighting of another "a"
select topn_add(topn_add('{}', 'a'), 'a');
-- => {"a": 2}
A extensão também fornece agregações para verificar vários valores:
-- for normal_rand
create extension tablefunc;
-- count values from a normal distribution
SELECT topn_add_agg(floor(abs(i))::text)
FROM normal_rand(1000, 5, 0.7) i;
-- => {"2": 1, "3": 74, "4": 420, "5": 425, "6": 77, "7": 3}
Se o número de valores distintos ultrapassar um limite, a agregação descartará informações para esses valores vistos com menos frequência, mantendo o uso de espaço sob controle. Você pode controlar o limite usando a configuração topn.number_of_countersdo usuário global. Seu valor padrão é 1000.
Exemplo realista
Agora, em um exemplo mais realista de como o TopN funciona na prática. Vamos ingerir as revisões de produtos da Amazon do ano 2000 e usar o TopN para consultá-lo rapidamente. Primeiro, baixe o conjunto de dados:
curl -L https://examples.citusdata.com/customer_reviews_2000.csv.gz | \
gunzip > reviews.csv
Em seguida, ingera-o em uma tabela distribuída:
CREATE TABLE customer_reviews
(
customer_id TEXT,
review_date DATE,
review_rating INTEGER,
review_votes INTEGER,
review_helpful_votes INTEGER,
product_id CHAR(10),
product_title TEXT,
product_sales_rank BIGINT,
product_group TEXT,
product_category TEXT,
product_subcategory TEXT,
similar_product_ids CHAR(10)[]
);
SELECT create_distributed_table('customer_reviews', 'product_id');
\COPY customer_reviews FROM 'reviews.csv' WITH CSV
Em seguida, adicione a extensão, crie uma tabela de destino para armazenar os dados json gerados pelo TopN e aplique a topn_add_agg função que você viu anteriormente.
-- run below command from coordinator, it will be propagated to the worker nodes as well
CREATE EXTENSION topn;
-- a table to materialize the daily aggregate
CREATE TABLE reviews_by_day
(
review_date date unique,
agg_data jsonb
);
SELECT create_reference_table('reviews_by_day');
-- materialize how many reviews each product got per day per customer
INSERT INTO reviews_by_day
SELECT review_date, topn_add_agg(product_id)
FROM customer_reviews
GROUP BY review_date;
Agora, em vez de escrever uma função customer_reviewsde janela complexa, você pode aplicar TopN a reviews_by_day. Por exemplo, a consulta a seguir localiza o produto revisado com mais frequência para cada um dos primeiros cinco dias:
SELECT review_date, (topn(agg_data, 1)).*
FROM reviews_by_day
ORDER BY review_date
LIMIT 5;
┌─────────────┬────────────┬───────────┐
│ review_date │ item │ frequency │
├─────────────┼────────────┼───────────┤
│ 2000-01-01 │ 0939173344 │ 12 │
│ 2000-01-02 │ B000050XY8 │ 11 │
│ 2000-01-03 │ 0375404368 │ 12 │
│ 2000-01-04 │ 0375408738 │ 14 │
│ 2000-01-05 │ B00000J7J4 │ 17 │
└─────────────┴────────────┴───────────┘
Os campos JSON criados pelo TopN podem ser mesclados com topn_union e topn_union_agg. Você pode usar este último para mesclar os dados durante todo o primeiro mês e listar os cinco produtos mais revisados durante esse período.
SELECT (topn(topn_union_agg(agg_data), 5)).*
FROM reviews_by_day
WHERE review_date >= '2000-01-01' AND review_date < '2000-02-01'
ORDER BY 2 DESC;
┌────────────┬───────────┐
│ item │ frequency │
├────────────┼───────────┤
│ 0375404368 │ 217 │
│ 0345417623 │ 217 │
│ 0375404376 │ 217 │
│ 0375408738 │ 217 │
│ 043936213X │ 204 │
└────────────┴───────────┘
Para obter mais informações e exemplos, consulte o leiame do TopN.
Cálculos de percentil
Encontrar um percentil exato em um grande número de linhas pode ser muito caro. O coordenador deve receber todas as linhas para classificação final e processamento. Por outro lado, você pode encontrar uma aproximação em paralelo em nós de trabalho usando um algoritmo de esboço. O nó coordenador combina resumos compactados no resultado final, em vez de ler as linhas completas.
Um algoritmo de esboço popular para percentis usa uma estrutura de dados compactada chamada tdigeste está disponível para PostgreSQL na extensão t-digest. O Citus contém suporte integrado para essa extensão.
Veja como usar tdigest no Citus:
Baixe e instale a
tdigestextensão em todos os nós do PostgreSQL (o coordenador e todos os trabalhadores). O repositório GitHub da extensão t-digest tem instruções de instalação.Crie a
tdigestextensão dentro do banco de dados. Execute o seguinte comando no coordenador:CREATE EXTENSION tdigest;O coordenador propaga o comando para os trabalhadores também.
Quando você usa qualquer uma das agregações definidas na extensão em consultas, o Citus reescreve as consultas para efetuar push da computação parcial tdigest para os trabalhadores, quando aplicável.
Você pode controlar a precisão com tdigest o compression argumento que você passa para agregações. A compensação é a precisão versus a quantidade de dados compartilhados entre os trabalhadores e o coordenador. Para obter uma explicação completa de como usar as agregações na tdigest extensão, consulte a documentação no repositório gitHub da extensão t-digest oficial.
Limitar pushdown
O Citus também reduz as cláusulas de limite para os fragmentos nos trabalhos sempre que possível para minimizar a quantidade de dados transferidos pela rede.
No entanto, em alguns casos, SELECT as consultas com LIMIT cláusulas podem precisar buscar todas as linhas de cada fragmento para gerar resultados exatos. Por exemplo, se a consulta exigir ordenação pela coluna de agregação, ela precisará de resultados dessa coluna de todos os fragmentos para determinar o valor de agregação final. Esse processamento reduz o LIMIT desempenho da cláusula devido ao alto volume de transferência de dados de rede. Nesses casos e em que uma aproximação produziria resultados significativos, o Citus fornece uma opção para cláusulas aproximadas LIMIT com eficiência de rede.
LIMIT As aproximações são desabilitadas por padrão. Você pode habilitá-los definindo o parâmetro citus.limit_clause_row_fetch_countde configuração. Com base nesse valor de configuração, o Citus limita o número de linhas retornadas por cada tarefa para agregação no coordenador. Devido a esse limite, os resultados finais podem ser aproximados. Aumentar esse limite aumenta a precisão dos resultados finais, ao mesmo tempo em que fornece um limite superior no número de linhas extraídas dos trabalhadores.
SET citus.limit_clause_row_fetch_count to 10000;
Exibições em tabelas distribuídas
O Citus dá suporte a todas as exibições em tabelas distribuídas. Para obter uma visão geral da sintaxe e dos recursos das exibições, consulte a documentação do PostgreSQL para CREATE VIEW.
Alguns modos de exibição causam um plano de consulta menos eficiente do que outros. Para obter mais informações sobre como detectar e melhorar o desempenho de exibição ruim, consulte subquery_perf. (As exibições são tratadas internamente como subconsultas.)
O Citus também dá suporte a exibições materializadas e as armazena como tabelas locais no nó coordenador.
Joins
O Citus dá suporte a equi-JOINs entre qualquer número de tabelas, independentemente de seu tamanho e método de distribuição. Com base em como as tabelas são distribuídas, o planejador de consultas determina o método de junção ideal e a ordem de junção. Ele avalia vários pedidos de junção possíveis e cria um plano de junção, que exige que os dados mínimos sejam transferidos pela rede.
Junções colocatadas
Quando duas tabelas são colocadas, você pode juntá-las com eficiência em suas colunas de distribuição comuns. Uma junção colocatada é a maneira mais eficiente de unir duas grandes tabelas distribuídas.
Internamente, o coordenador do Citus sabe quais fragmentos das tabelas colocadas podem corresponder com fragmentos da outra tabela examinando os metadados da coluna de distribuição. Esses metadados permitem que o Citus tire os pares de fragmentos que não podem produzir chaves de junção correspondentes. As junções entre os pares de fragmentos restantes são executadas em paralelo nos trabalhos e, em seguida, os resultados são retornados ao coordenador.
Observação
Verifique se as tabelas são distribuídas no mesmo número de fragmentos e se as colunas de distribuição de cada tabela têm exatamente tipos correspondentes. A tentativa de ingressar em colunas de tipos ligeiramente diferentes, como int e bigint, pode causar problemas.
Associações de tabela de referência
Use reference_tables como tabelas de dimensão para unir com eficiência tabelas de fatos grandes. Como o Citus replica as tabelas de referência na íntegra em todos os nós de trabalho, uma junção de referência pode se decompor em junções locais em cada trabalho e executada em paralelo. Uma junção de referência é como uma versão mais flexível de uma junção colocatada porque as tabelas de referência não são distribuídas em nenhuma coluna específica e são livres para ingressar em qualquer uma de suas colunas.
As tabelas de referência também podem unir tabelas locais ao nó coordenador.
Pushdown de junção externa (Citus 13.2)
O Citus 13.2 pode reduzir as junções externas esquerda e direita qualificadas para os trabalhadores quando o lado externo é uma relação recorrente (por exemplo, tabelas de referência ou resultados intermediários). O planejador injeta restrições de intervalo de fragmentos no lado recorrente para garantir a correção, para que a junção possa ser executada em trabalhos e evitar junções do lado do coordenador e grandes resultados intermediários.
- Habilitado por padrão. Desabilitar usando
citus.enable_recurring_outer_join_pushdown. - Aplica-se a junções externas recorrentes em que intervalos de fragmentos podem ser derivados (normalmente tabelas distribuídas por hash), incluindo tabelas de referência, resultados intermediários e funções de retorno de conjunto no lado externo.
- O Planner injeta restrições derivadas de intervalos de hash de fragmento na verificação lateral recorrente (referência, intermediária ou SRF).
- O Planner verifica as regras de qualificação (consulte
CanPushdownRecurringOuterJoina fonte citus para obter detalhes).
Exemplo:
EXPLAIN (COSTS OFF)
SELECT *
FROM product_categories pc
LEFT JOIN products_table pt
ON pc.category_id = pt.product_id;
Antes da 13.2 (a junção de referência não foi enviada por push para baixo):
Custom Scan (Citus Adaptive)
-> Distributed Subplan ...
-> Task
-> Hash Right Join
Hash Cond: (intermediate_result.product_id = pc.category_id)
Citus 13.2 (empurrado para baixo com restrições):
Custom Scan (Citus Adaptive)
Task Count: 32
-> Task
Node: host=localhost port=9701 dbname=postgres
-> Hash Right Join
Hash Cond: (pt.product_id = pc.category_id)
-> Seq Scan on products_table_102072 pt
-> Hash
-> Seq Scan on product_categories_102106 pc
Filter: ((category_id IS NULL) OR (btint4cmp('-2147483648', hashint8(category_id::bigint)) < 0
AND btint4cmp(hashint8(category_id::bigint), '-2013265921') <= 0))
Repartition joins
Em alguns casos, você precisa unir duas tabelas em colunas diferentes da coluna de distribuição. Para esses casos, o Citus também permite a junção em colunas de chave nondistribution reparticionando dinamicamente as tabelas para a consulta.
Nesses casos, o otimizador de consulta determina as tabelas a serem particionadas com base nas colunas de distribuição, nas chaves de junção e nos tamanhos das tabelas. Com tabelas reparticionadas, você pode garantir que apenas pares de fragmentos relevantes se unam, o que reduz drasticamente a quantidade de dados transferidos pela rede.
Em geral, as junções colocacionadas são mais eficientes do que as junções de repartição, pois as junções de repartição exigem embaralhamento de dados. Portanto, tente distribuir suas tabelas pelas chaves de junção comuns sempre que possível.