Condividi tramite


Esercitazione: Partizionare i dati nei nodi di lavoro in Azure Cosmos DB for PostgreSQL

SI APPLICA A: Azure Cosmos DB for PostgreSQL (basato sull'estensione di database Citus per PostgreSQL)

In questa esercitazione si usa Azure Cosmos DB for PostgreSQL per imparare a:

  • Creare partizioni con distribuzione hash
  • Vedere dove vengono inserite le partizioni di tabelle
  • Identificare la distribuzione asimmetrica
  • Creare vincoli nelle tabelle distribuite
  • Eseguire query sui dati distribuiti

Prerequisiti

Questa esercitazione richiede un cluster in esecuzione con due nodi di lavoro. Se non è disponibile un cluster in esecuzione, seguire questa l'esercitazione per la creazione di un cluster prima di procedere.

Dati con distribuzione hash

La distribuzione di righe di tabella in più server PostgreSQL è una tecnica fondamentale per le query scalabili in Azure Cosmos DB for PostgreSQL. Più nodi possono contenere più dati rispetto a un database tradizionale e in molti casi possono usare le CPU di lavoro in parallelo per eseguire query. Il concetto di tabelle con distribuzione hash è noto anche come partizionamento orizzontale basato su righe.

Nella sezione dei prerequisiti è stato creato un cluster con due nodi di lavoro.

Nodo coordinatore e due nodi di lavoro

Le tabelle di metadati del nodo coordinatore tengono traccia dei dati dei nodi di lavoro e di quelli distribuiti. È possibile controllare i nodi di lavoro attivi nella tabella 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

I nomi dei nodi in AZure Cosmos DB for PostgreSQL sono indirizzi IP interni di una rete virtuale e gli indirizzi effettivi possono variare.

Righe, partizioni e posizionamenti

Per usare le risorse di CPU e archiviazione dei nodi di lavoro, è necessario distribuire i dati delle tabelle in tutto il cluster. La distribuzione di una tabella assegna ogni riga a un gruppo logico denominato partizione. Creare una tabella e distribuirla:

-- 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 assegna ogni riga a una partizione in base al valore della colonna di distribuzione, che in questo caso è stata impostata su email. Ogni riga verrà inserita in una sola partizione e ogni partizione può contenere più righe.

Tabella users con le righe che puntano alle partizioni

Per impostazione predefinita, create_distributed_table() crea 32 partizioni, come si può verificare conteggiando il numero nella tabella di metadati pg_dist_shard:

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

Azure Cosmos DB for PostgreSQL usa la tabella pg_dist_shard per assegnare righe alle partizioni, in base a un hash del valore nella colonna di distribuzione. I dettagli degli hash sono irrilevanti per questa esercitazione. Quello che conta è che è possibile eseguire una query per verificare il mapping tra i valori e gli ID partizione:

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

Il mapping delle righe alle partizioni è puramente logico. Le partizioni devono essere assegnate a specifici nodi di lavoro per l'archiviazione, in base a quello che in Azure Cosmos DB for PostgreSQL viene definito posizionamento delle partizioni.

Partizioni assegnate ai nodi di lavoro

È possibile esaminare i posizionamenti delle partizioni in pg_dist_placement. Unendo questa tabella alle altre tabelle di metadati, è possibile vedere dove risiede ogni partizione.

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

Asimmetria dei dati

Un cluster viene eseguito in modo più efficiente se i dati vengono posizionati uniformemente nei nodi di lavoro e se i dati correlati vengono inseriti insieme negli stessi nodi di lavoro. In questa sezione verrà illustrata la prima parte, ovvero l'uniformità del posizionamento.

Per verificarlo, creare dati di esempio per la tabella 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);

Per vedere le dimensioni delle partizioni, è possibile eseguire funzioni di dimensionamento delle tabelle.

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

Come si può notare, le partizioni hanno dimensioni uguali. Dato che i posizionamenti sono distribuiti uniformemente tra i nodi di lavoro, si può dedurre che i nodi di lavoro contengono approssimativamente lo stesso numero di righe.

Le righe nell'esempio users sono distribuite uniformemente a causa delle proprietà della colonna di distribuzione email.

  1. Il numero di indirizzi di posta elettronica è maggiore o uguale al numero di partizioni.
  2. Il numero di righe per ogni indirizzo di posta elettronica è simile (in questo caso, esattamente una riga per ogni indirizzo, perché la colonna email è stata dichiarata come chiave).

Qualsiasi scelta di tabella e colonna di distribuzione in cui una delle proprietà ha esito negativo comporterà dimensioni dei dati non uniformi nei nodi di lavoro, ovvero una asimmetria dei dati.

Aggiungere vincoli ai dati distribuiti

L'uso di Azure Cosmos DB for PostgreSQL consente di continuare a sfruttare la sicurezza di un database relazionale, inclusi i vincoli di database. Tuttavia, esiste una limitazione. Data la natura dei sistemi distribuiti, Azure Cosmos DB for PostgreSQL non farà riferimento a vincoli di univocità tra riferimenti o all’integrità referenziale tra nodi di lavoro.

Si consideri l'esempio di tabella users con una tabella correlata.

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

Per una maggiore efficienza, distribuire books allo stesso modo di users, ovvero in base all'indirizzo di posta elettronica del proprietario. La distribuzione in base a valori di colonna simili viene chiamata coubicazione.

Non si sono verificati problemi con la distribuzione di libri con una chiave esterna agli utenti, perché la chiave si trovava in una colonna di distribuzione. Tuttavia, si verificherebbero problemi impostando isbn come chiave:

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

In una tabella distribuita è consigliabile rendere le colonne univoche rispetto alla colonna di distribuzione:

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

Il vincolo precedente rende semplicemente il valore di isbn univoco per ogni utente. Un'altra opzione consiste nel creare una tabella di riferimento per i libri anziché una tabella distribuita e creare una tabella distribuita separata che associa i libri agli utenti.

Eseguire query sulle tabelle distribuite

Nelle sezioni precedenti è stato illustrato come posizionare le righe di tabelle distribuite in partizioni nei nodi di lavoro. Il più delle volte non è necessario sapere come o dove vengono archiviati i dati in un cluster. Azure Cosmos DB for PostgreSQL include un executor di query distribuite che divide automaticamente le normali query SQL. Le esegue in parallelo nei nodi di lavoro vicino ai dati.

È ad esempio possibile eseguire una query per individuare l'età media degli utenti, trattando la tabella distribuita users come una normale tabella nel nodo coordinatore.

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

Esecuzione di query sulle partizioni tramite nodo coordinatore

Dietro le quinte, l'executor di Azure Cosmos DB for PostgreSQL crea una query distinta per ogni partizione, le esegue tutte nei nodi di lavoro e combina il risultato. È possibile verificarlo usando il comando EXPLAIN di PostgreSQL:

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)

L'output mostra un esempio di piano di esecuzione per un frammento di query in esecuzione nella partizione 102040 (tabella users_102040 nel nodo di lavoro 10.0.0.21). Gli altri frammenti non vengono mostrati perché sono simili. Si noterà che il nodo di lavoro analizza le tabelle di partizioni e applica l'aggregazione. Il nodo coordinatore combina le aggregazioni per il risultato finale.

Passaggi successivi

In questa esercitazione è stata creata una tabella distribuita e sono state descritti i concetti di partizioni e posizionamenti. Sono stati evidenziati i problemi associati all'uso di vincoli di univocità e di chiave esterna e infine è stato illustrato il funzionamento generale delle query distribuite.