Partilhar via


Tutorial: Fragmentar dados em nós de trabalho no Azure Cosmos DB para PostgreSQL

APLICA-SE A: Azure Cosmos DB para PostgreSQL (alimentado pela extensão de banco de dados Citus para PostgreSQL)

Neste tutorial, você usa o Azure Cosmos DB para PostgreSQL para aprender a:

  • Criar extensões distribuídas por hash
  • Veja onde os estilhaços de mesa são colocados
  • Identificar distribuição distorcida
  • Criar restrições em tabelas distribuídas
  • Executar consultas em dados distribuídos

Pré-requisitos

Este tutorial requer um cluster em execução com dois nós de trabalho. Se você não tiver um cluster em execução, siga o tutorial de criação de cluster e volte para este.

Dados distribuídos por hash

Distribuir linhas de tabela em vários servidores PostgreSQL é uma técnica fundamental para consultas escaláveis no Azure Cosmos DB para PostgreSQL. Juntos, vários nós podem armazenar mais dados do que um banco de dados tradicional e, em muitos casos, podem usar CPUs de trabalho em paralelo para executar consultas. O conceito de tabelas distribuídas por hash também é conhecido como fragmentação baseada em linha.

Na seção de pré-requisitos, criamos um cluster com dois nós de trabalho.

coordenador e dois trabalhadores

As tabelas de metadados do nó coordenador rastreiam trabalhadores e dados distribuídos. Podemos verificar os trabalhadores ativos na tabela pg_dist_node.

select nodeid, nodename from pg_dist_node where isactive;
 nodeid | nodename
--------+-----------
      1 | 10.0.0.21
      2 | 10.0.0.23

Nota

Os nomes de nó no Azure Cosmos DB para PostgreSQL são endereços IP internos em uma rede virtual e os endereços reais que você vê podem diferir.

Linhas, fragmentos e posicionamentos

Para usar a CPU e os recursos de armazenamento dos nós de trabalho, temos que distribuir os dados da tabela em todo o cluster. A distribuição de uma tabela atribui cada linha a um grupo lógico chamado fragmento. Vamos criar uma tabela e distribuí-la:

-- create a table on the coordinator
create table users ( email text primary key, bday date not null );

-- distribute it into shards on workers
select create_distributed_table('users', 'email');

O Azure Cosmos DB para PostgreSQL atribui cada linha a um fragmento com base no valor da coluna de distribuição, que, no nosso caso, especificámos como email. Cada linha estará em exatamente um fragmento, e cada fragmento pode conter várias linhas.

Tabela de usuários com linhas apontando para estilhaços

Por padrão create_distributed_table() faz 32 fragmentos, como podemos ver contando na tabela de metadados pg_dist_shard:

select logicalrelid, count(shardid)
  from pg_dist_shard
 group by logicalrelid;
 logicalrelid | count
--------------+-------
 users        |    32

O Azure Cosmos DB para PostgreSQL usa a pg_dist_shard tabela para atribuir linhas a fragmentos, com base em um hash do valor na coluna de distribuição. Os detalhes do hashing não são importantes para este tutorial. O que importa é que podemos consultar para ver quais valores mapeiam para quais IDs de estilhaços:

-- Where would a row containing hi@test.com be stored?
-- (The value doesn't have to actually be present in users, the mapping
-- is a mathematical operation consulting pg_dist_shard.)
select get_shard_id_for_distribution_column('users', 'hi@test.com');
 get_shard_id_for_distribution_column
--------------------------------------
                               102008

O mapeamento de linhas para fragmentos é puramente lógico. Os fragmentos devem ser atribuídos a nós de trabalho específicos para armazenamento, no que o Azure Cosmos DB para PostgreSQL chama de posicionamento de estilhaço.

estilhaços atribuídos aos trabalhadores

Podemos olhar para as colocações de estilhaços em pg_dist_placement. Juntá-lo com as outras tabelas de metadados que vimos mostra onde cada fragmento vive.

-- limit the output to the first five placements

select
	shard.logicalrelid as table,
	placement.shardid as shard,
	node.nodename as host
from
	pg_dist_placement placement,
	pg_dist_node node,
	pg_dist_shard shard
where placement.groupid = node.groupid
  and shard.shardid = placement.shardid
order by shard
limit 5;
 table | shard  |    host
-------+--------+------------
 users | 102008 | 10.0.0.21
 users | 102009 | 10.0.0.23
 users | 102010 | 10.0.0.21
 users | 102011 | 10.0.0.23
 users | 102012 | 10.0.0.21

Distorção de dados

Um cluster é executado de forma mais eficiente quando você coloca dados uniformemente em nós de trabalho e quando coloca dados relacionados juntos nos mesmos trabalhadores. Nesta seção, vamos nos concentrar na primeira parte, a uniformidade da colocação.

Para demonstrar, vamos criar dados de exemplo para nossa users tabela:

-- load sample data
insert into users
select
	md5(random()::text) || '@test.com',
	date_trunc('day', now() - random()*'100 years'::interval)
from generate_series(1, 1000);

Para ver os tamanhos dos estilhaços, podemos executar funções de tamanho de tabela nos fragmentos.

-- sizes of the first five shards
select *
from
	run_command_on_shards('users', $cmd$
	  select pg_size_pretty(pg_table_size('%1$s'));
	$cmd$)
order by shardid
limit 5;
 shardid | success | result
---------+---------+--------
  102008 | t       | 16 kB
  102009 | t       | 16 kB
  102010 | t       | 16 kB
  102011 | t       | 16 kB
  102012 | t       | 16 kB

Podemos ver que os fragmentos são de igual tamanho. Já vimos que as colocações são distribuídas uniformemente entre os trabalhadores, de modo que podemos inferir que os nós de trabalhador possuem números aproximadamente iguais de linhas.

As linhas em nosso users exemplo distribuídas uniformemente porque as propriedades da coluna de distribuição, email.

  1. O número de endereços de e-mail era maior ou igual ao número de fragmentos.
  2. O número de linhas por endereço de e-mail foi semelhante (no nosso caso, exatamente uma linha por endereço, porque declaramos o e-mail uma chave).

Qualquer escolha de tabela e coluna de distribuição em que qualquer propriedade falhar acabará com o tamanho de dados desigual nos trabalhadores, ou seja, a distorção de dados.

Adicionar restrições aos dados distribuídos

Usar o Azure Cosmos DB para PostgreSQL permite que você continue a aproveitar a segurança de um banco de dados relacional, incluindo restrições de banco de dados. No entanto, há uma limitação. Devido à natureza dos sistemas distribuídos, o Azure Cosmos DB para PostgreSQL não fará referência cruzada a restrições de exclusividade ou integridade referencial entre nós de trabalho.

Vamos considerar nosso users exemplo de tabela com uma tabela relacionada.

-- books that users own
create table books (
	owner_email text references users (email),
	isbn text not null,
	title text not null
);

-- distribute it
select create_distributed_table('books', 'owner_email');

Para maior eficiência, distribuímos books da mesma forma que users: pelo endereço de e-mail do proprietário. A distribuição por valores de coluna semelhantes é chamada de colocation.

Não tivemos nenhum problema em distribuir livros com uma chave estrangeira para os usuários, porque a chave estava em uma coluna de distribuição. No entanto, teríamos dificuldade em fazer isbn uma chave:

-- will not work
alter table books add constraint books_isbn unique (isbn);
ERROR:  cannot create constraint on "books"
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or
        PRIMARY KEY constraints that do not include the partition column
        (with an equality operator if EXCLUDE).

Em uma tabela distribuída, o melhor que podemos fazer é tornar as colunas únicas modulo a coluna de distribuição:

-- a weaker constraint is allowed
alter table books add constraint books_isbn unique (owner_email, isbn);

A restrição acima apenas torna o isbn único por usuário. Outra opção é tornar os livros uma tabela de referência em vez de uma tabela distribuída e criar uma tabela distribuída separada associando livros aos usuários.

Consultar tabelas distribuídas

Nas seções anteriores, vimos como as linhas de tabela distribuídas são colocadas em fragmentos nos nós de trabalho. Na maioria das vezes, você não precisa saber como ou onde os dados são armazenados em um cluster. O Azure Cosmos DB para PostgreSQL tem um executor de consulta distribuído que divide automaticamente as consultas SQL regulares. Ele os executa em paralelo em nós de trabalho próximos aos dados.

Por exemplo, podemos executar uma consulta para encontrar a idade média dos usuários, tratando a tabela distribuída users como se fosse uma tabela normal no coordenador.

select avg(current_date - bday) as avg_days_old from users;
    avg_days_old
--------------------
 17926.348000000000

consulta indo para fragmentos via coordenador

Nos bastidores, o executor do Azure Cosmos DB para PostgreSQL cria uma consulta separada para cada fragmento, executa-os nos trabalhadores e combina o resultado. Você pode vê-lo se você usar o comando PostgreSQL EXPLICAR:

explain select avg(current_date - bday) from users;
                                  QUERY PLAN
----------------------------------------------------------------------------------
 Aggregate  (cost=500.00..500.02 rows=1 width=32)
   ->  Custom Scan (Citus Adaptive)  (cost=0.00..0.00 rows=100000 width=16)
     Task Count: 32
     Tasks Shown: One of 32
     ->  Task
       Node: host=10.0.0.21 port=5432 dbname=citus
       ->  Aggregate  (cost=41.75..41.76 rows=1 width=16)
         ->  Seq Scan on users_102040 users  (cost=0.00..22.70 rows=1270 width=4)

A saída mostra um exemplo de um plano de execução para um fragmento de consulta em execução no fragmento 102040 (a tabela users_102040 no trabalhador 10.0.0.21). Os outros fragmentos não são mostrados porque são semelhantes. Podemos ver que o nó de trabalho verifica as tabelas de estilhaços e aplica o agregado. O nó coordenador combina agregados para o resultado final.

Próximos passos

Neste tutorial, criamos uma tabela distribuída e aprendemos sobre seus fragmentos e posicionamentos. Vimos um desafio de usar exclusividade e restrições de chave estrangeira e, finalmente, vimos como as consultas distribuídas funcionam em alto nível.