Colocação de tabela no Azure Cosmos DB for PostgreSQL

APLICA-SE AO: Azure Cosmos DB for PostgreSQL (da plataforma da extensão de dados Citus para PostgreSQL)

Colocação significa armazenar informações relacionadas em conjunto nos mesmos nós. As consultas podem ser rápidas quando todos os dados necessários estão disponíveis sem nenhum tráfego de rede. A colocação de dados relacionados em nós diferentes permite que as consultas executem com eficiência em paralelo em cada nó.

Colocação de dados para tabelas distribuídas por hash

No Azure Cosmos DB for PostgreSQL, uma linha é armazenada em um fragmento quando o hash do valor na coluna de distribuição está dentro do intervalo de hash do fragmento. Fragmentos com o mesmo intervalo de hash sempre são colocados no mesmo nó. Linhas com valores de coluna de distribuição iguais estão sempre no mesmo nó entre tabelas. O conceito de tabelas distribuídas por fragmentos também é conhecido como fragmentação baseada em linhas. Em tabelas baseadas em fragmentação, as tabelas dentro de um esquema distribuído são sempre colocalizadas.

Diagram shows shards with the same hash range placed on the same node for events shards and page shards.

Um exemplo prático de colocação

Considere as seguintes tabelas que podem fazer parte de um SaaS de análise da Web de vários locatário:

CREATE TABLE event (
  tenant_id int,
  event_id bigint,
  page_id int,
  payload jsonb,
  primary key (tenant_id, event_id)
);

CREATE TABLE page (
  tenant_id int,
  page_id int,
  path text,
  primary key (tenant_id, page_id)
);

Agora, queremos responder a consultas que podem ser emitidas por um painel voltado para o cliente. Um exemplo de consulta é "Retornar o número de visitas na última semana para todas as páginas começando com '/blog' no locatário seis".

Se os dados estiverem em um servidor PostgreSQL único, será possível expressar a consulta com facilidade usando o conjunto completo de operações relacionais oferecido pelo SQL:

SELECT page_id, count(event_id)
FROM
  page
LEFT JOIN  (
  SELECT * FROM event
  WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

Desde que o conjunto de trabalho para essa consulta caiba na memória, uma tabela de servidor único é uma solução apropriada. Agora, considere as oportunidades de escala do modelo de dados com o Azure Cosmos DB for PostgreSQL.

Distribuir tabelas por ID

As consultas de servidor único começam a diminuir conforme o número de locatários e os dados armazenados para cada locatário crescem. O conjunto de trabalho para de se ajustar na memória e a CPU se torna um gargalo.

Nesse caso, é possível fragmentar os dados em vários nós usando o Azure Cosmos DB for PostgreSQL. A primeira e mais importante opção que precisamos fazer quando decidirmos o fragmento é a coluna de distribuição. Vamos começar com uma opção simples de usar event_id para a tabela de eventos e page_id para a page tabela:

-- naively use event_id and page_id as distribution columns

SELECT create_distributed_table('event', 'event_id');
SELECT create_distributed_table('page', 'page_id');

Quando os dados são dispersos em diferentes trabalhadores, não podemos realizar uma junção como faria em um único nó PostgreSQL. Em vez disso, precisamos emitir duas consultas:

-- (Q1) get the relevant page_ids
SELECT page_id FROM page WHERE path LIKE '/blog%' AND tenant_id = 6;

-- (Q2) get the counts
SELECT page_id, count(*) AS count
FROM event
WHERE page_id IN (/*…page IDs from first query…*/)
  AND tenant_id = 6
  AND (payload->>'time')::date >= now() - interval '1 week'
GROUP BY page_id ORDER BY count DESC LIMIT 10;

Posteriormente, os resultados das duas etapas precisam ser combinados pelo aplicativo.

Executar as consultas deve consultar dados em fragmentos espalhados entre nós.

Diagram shows an inefficient approach that uses multiple queries against the event and page tables in two nodes.

Nesse caso, a distribuição de dados cria desvantagens substanciais:

  • Sobrecarga da consulta de cada fragmento e execução de várias consultas.
  • Sobrecarga de Q1 retornando muitas linhas para o cliente.
  • Q2 torna-se grande.
  • A necessidade de escrever consultas em várias etapas requer alterações no aplicativo.

Os dados são dispersos, portanto, as consultas podem ser paralelizadas. Só é benéfico se a quantidade de trabalho que a consulta faz é consideravelmente maior do que a sobrecarga de consultar muitos fragmentos.

Distribuir tabelas por locatário

No Azure Cosmos DB for PostgreSQL, é garantido que as linhas com o mesmo valor na coluna de distribuição estejam no mesmo nó. A partir de então, podemos criar nossas tabelas com tenant_id como a coluna de distribuição.

-- co-locate tables by using a common distribution column
SELECT create_distributed_table('event', 'tenant_id');
SELECT create_distributed_table('page', 'tenant_id', colocate_with => 'event');

Agora, o Azure Cosmos DB for PostgreSQL pode responder à consulta de servidor único original sem modificação (P1):

SELECT page_id, count(event_id)
FROM
  page
LEFT JOIN  (
  SELECT * FROM event
  WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

Devido ao filtro e à junção em tenant_id, o Azure Cosmos DB for PostgreSQL sabe que toda a consulta pode ser respondida usando o conjunto de fragmentos colocados que contêm os dados desse locatário específico. Um único nó PostgreSQL pode responder à consulta em uma única etapa.

Diagram shows a single query to one node, which is a more efficient approach.

Em alguns casos, consultas e esquemas de tabela devem ser alterados para incluir a ID do locatário em restrições exclusivas e condições de junção. Essa alteração geralmente é simples.

Próximas etapas