Partager via


Tutoriel : Partitionner des données sur des nœuds Worker dans Azure Cosmos DB for PostgreSQL

S’APPLIQUE À : Azure Cosmos DB for PostgreSQL (avec l’extension de base de données Citus pour PostgreSQL)

Dans ce tutoriel, vous allez utiliser Azure Cosmos DB pour PostgreSQL pour découvrir comment :

  • Créer des tables distribuées par hachage
  • Voir où les partitions de table sont placées
  • Identifier une distribution non uniforme
  • Créer des contraintes sur des tables distribuées
  • Exécuter des requêtes sur des données distribuées

Prérequis

Ce tutoriel nécessite un cluster en cours d’exécution avec deux nœuds Worker. Si vous n’avez pas de cluster en cours d’exécution, suivez le tutoriel Créer un cluster, puis revenez à celui-ci.

Données distribuées par hachage

La distribution des lignes de la table sur plusieurs serveurs PostgreSQL est une technique clé pour les requêtes évolutives dans Azure Cosmos DB for PostgreSQL. Ensemble, plusieurs nœuds peuvent contenir plus de données qu’une base de données traditionnelle et, dans de nombreux cas, ils peuvent utiliser les processeurs des Workers en parallèle pour exécuter des requêtes. Le concept de tables de hachage distribuées est également appelé partitionnement basé sur les lignes.

Dans la section Prérequis, nous avons créé un cluster avec deux nœuds Worker.

coordinateur et deux Workers

Les tables de métadonnées du nœud coordinateur effectuent le suivi des Workers et des données distribuées. Nous pouvons vérifier les Workers actifs dans la table pg_dist_node.

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

Notes

Les noms de nœuds sur Azure Cosmos DB for PostgreSQL sont des adresses IP internes dans un réseau virtuel, et les adresses réelles que vous voyez peuvent être différentes.

Lignes, partitions et sélections électives

Pour utiliser les ressources de processeur et de stockage des nœuds Worker, nous devons distribuer les données de table dans le cluster. La distribution d’une table affecte chaque ligne à un groupe logique appelé partition. Nous allons créer une table et la distribuer :

-- 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');

Azure Cosmos DB for PostgreSQL attribue chaque ligne à une partition en fonction de la valeur de la colonne de distribution, que nous avons spécifiée ici comme email. Chaque ligne figurera dans exactement une partition et chaque partition peut contenir plusieurs lignes.

table Users avec des lignes pointant vers des partitions

Par défaut, create_distributed_table() crée 32 partitions, comme nous pouvons le voir en les comptant dans la table de métadonnées pg_dist_shard :

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

Azure Cosmos DB for PostgreSQL utilise la table pg_dist_shard pour assigner des lignes aux partitions, en fonction d’un hachage de la valeur dans la colonne de distribution. Les détails de hachage n’ont pas d’importance pour ce tutoriel. Il est important de noter que nous pouvons effectuer des requêtes pour voir quelles valeurs sont mappées aux ID de partitions :

-- 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

Le mappage des lignes aux partitions est purement logique. Les partitions doivent être affectées à des nœuds Worker spécifiques à des fins de stockage dans ce qu’Azure Cosmos DB for PostgreSQL appelle une sélection élective des partitions.

partitions assignées aux Workers

Nous pouvons examiner les sélections électives des partitions dans pg_dist_placement. La jointure de cela aux autres tables de métadonnées que nous avons vues montre où chaque partition se trouve.

-- 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

Asymétrie des données

Un serveur s’exécute plus efficacement lorsque vous placez des données uniformément sur les nœuds Worker, et lorsque vous placez les données associées sur les mêmes Workers. Dans cette section, nous allons nous concentrer sur la première partie, l’uniformité de la sélection élective.

Pour illustrer cela, nous allons créer des exemples de données pour notre table users :

-- 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);

Pour voir les tailles des partitions, nous pouvons exécuter des fonctions de taille de table sur les partitions.

-- 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

Nous pouvons voir que les partitions sont de taille égale. Nous avons déjà vu que les sélections électives sont réparties uniformément entre les Workers, de sorte que nous pouvons déduire que les nœuds Worker contiennent à peu près le même nombre de lignes.

Les lignes de notre exemple users sont distribuées uniformément en raison des propriétés de la colonne de distribution, email.

  1. Le nombre d’adresses e-mail était supérieur ou égal au nombre de partitions.
  2. Le nombre de lignes par adresse e-mail était similaire (dans notre cas, exactement une ligne par adresse parce que nous avons déclaré « email » comme clé).

Un choix quelconque de table et de colonne de distribution pour lequel l’une ou l’autre des propriétés échoue aboutira à une taille de données inégale sur les Workers, autrement dit, à une asymétrie des données.

Ajouter des contraintes aux données distribuées

L’utilisation d’Azure Cosmos DB for PostgreSQL vous permet de continuer de bénéficier de la sécurité d’une base de données relationnelle, y compris des contraintes de base de données. Toutefois, une limite s’applique. En raison de la nature des systèmes distribués, Azure Cosmos DB for PostgreSQL n’établit pas de références croisées entre les nœuds Worker pour les contraintes d’unicité et l’intégrité référentielle.

Prenons notre exemple de table users avec une table associée.

-- 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');

À des fins d’efficacité, nous distribuons books de la même façon que users : par l’adresse e-mail du propriétaire. La distribution par des valeurs de colonne similaires est appelée colocation.

Nous n’avons eu aucun problème à distribuer les livres avec une clé étrangère aux utilisateurs, car la clé était sur une colonne de distribution. Toutefois, nous aurions eu du mal à définir isbn comme clé :

-- 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).

Dans une table distribuée, le mieux que nous pouvons faire est de définir le modulo unique des colonnes comme colonne de distribution :

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

La contrainte ci-dessus rend simplement isbn unique par utilisateur. Une autre option consiste à faire de books une table de référence plutôt qu’une table distribuée, et à créer une table distribuée distincte qui associe les livres aux utilisateurs.

Interroger les tables distribuées

Dans les sections précédentes, nous avons vu comment les lignes des tables distribuées sont placées dans des partitions sur les nœuds Worker. La plupart du temps, vous n’avez pas besoin de savoir comment ni où les données sont stockées dans un cluster. Azure Cosmos DB for PostgreSQL dispose d’un exécuteur de requêtes distribuées qui fractionne automatiquement les requêtes SQL régulières. Il les exécute en parallèle sur les nœuds Worker à proximité des données.

Par exemple, nous pouvons exécuter une requête pour rechercher l’âge moyen des utilisateurs, en traitant la table users distribuée comme s’il s’agissait d’une table normale sur le coordinateur.

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

requête accédant aux partitions via le coordinateur

En arrière-plan, l’exécuteur Azure Cosmos DB for PostgreSQL crée une requête distincte pour chaque partition, les exécute sur les Workers et associe le résultat. Vous pouvez le voir en utilisant la commande PostgreSQL EXPLAIN :

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)

La sortie montre un exemple de plan d’exécution pour un fragment de requête s’exécutant sur la partition 102040 (la table users_102040 sur le Worker 10.0.0.21). Les autres fragments ne sont pas affichés, car ils sont similaires. Nous pouvons voir que le nœud Worker analyse les tables de partitions et applique l’agrégat. Le nœud coordinateur combine les agrégats pour fournir le résultat final.

Étapes suivantes

Dans ce tutoriel, nous avons créé une table distribuée et étudié ses partitions et les sélections électives. Nous avons vu le défi que représente l’utilisation de contraintes d’unicité et de clé étrangère, et nous avons vu comment les requêtes distribuées fonctionnent à un niveau élevé.