Share via


Tutorial: Particionamiento de datos en nodos de trabajo en Azure Cosmos DB for PostgreSQL

SE APLICA A: Azure Cosmos DB for PostgreSQL (con tecnología de la extensión de base de datos de Citus en PostgreSQL)

En este tutorial, usará Azure Cosmos DB for PostgreSQL para aprender a:

  • Crear particiones distribuidas de hash.
  • Ver dónde se colocan las particiones de tabla.
  • Identificar una distribución sesgada.
  • Crear restricciones en tablas distribuidas.
  • Ejecutar consultas en datos distribuidos.

Prerrequisitos

En este tutorial, se requiere un clúster en ejecución con dos nodos de trabajo. Si no tiene un clúster en ejecución, siga el tutorial de creación de un clúster y, luego, vuelva a este.

Distribución por hash

La distribución de las filas de la tabla entre varios servidores PostgreSQL es una técnica clave para la realización de consultas escalables en Azure Cosmos DB for PostgreSQL. Juntos, varios nodos pueden contener más datos que una base de datos tradicional y, en muchos casos, pueden usar CPU de trabajo en paralelo para ejecutar consultas. El concepto de tablas distribuidas por hash también se conoce como particionamiento basado en filas.

En la sección de requisitos previos, creamos un clúster con dos nodos de trabajo.

coordinator and two workers

Las tablas de metadatos del nodo de coordinador realizan el seguimiento de los trabajos y los datos distribuidos. Podemos consultar los trabajos activos en la tabla 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

En Azure Cosmos DB for PostgreSQL, los nombres de nodos son direcciones IP internas de una red virtual y las direcciones reales que se ven puede que no sean las mismas.

Filas, particiones y selecciones de ubicaciones

Para usar los recursos de almacenamiento y CPU de los nodos de trabajo, es preciso distribuir los datos de la tabla en todo el clúster. Al distribuir una tabla se asigna cada fila a un grupo lógico denominado partición. Vamos a crear una tabla y 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 asigna cada fila a una partición en función del valor de la columna de distribución, que, en nuestro caso, especificamos que sea email. Cada fila estará en una sola partición y cada partición puede contener varias filas.

users table with rows pointing to shards

De forma predeterminada create_distributed_table() crea 32 particiones, tal como se puede ver contando la tabla de metadatos 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 tabla pg_dist_shard para asignar filas a particiones, según un hash del valor de la columna de distribución. Los detalles del hash no son importantes para este tutorial. Lo que importa es que podemos realizar consultas para ver qué valores se asignan a cada identificador de partición:

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

La asignación de filas a particiones es puramente lógico. Se deben asignar particiones a nodos de trabajo concretos para el almacenamiento, en lo que Azure Cosmos DB for PostgreSQL llama selección de ubicación de partición.

shards assigned to workers

Las selecciones de ubicación de las particiones se pueden examinar en pg_dist_placement. Su unión con las otras tablas de metadatos que hemos visto muestra el lugar en que reside cada partición.

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

Asimetría de datos

Un clúster se ejecuta más eficazmente cuando se colocan los datos de manera uniforme en todos los nodos de trabajo y cuando se colocan los datos relacionados juntos en los mismos trabajos. En esta sección nos centraremos en la primera parte, la uniformidad de la ubicación.

Para mostrarla, se van a crear datos de ejemplo para la tabla 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);

Para ver los tamaños de las particiones, se pueden ejecutar funciones de tamaño de tabla en las particiones.

-- 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 todas las particiones tienen el mismo tamaño. Ya se ha visto que las ubicaciones se distribuyen uniformemente entre los trabajos, por lo que podemos deducir que los nodos de trabajo contienen aproximadamente el mismo número de filas.

Las filas de nuestro ejemplo de users se distribuyen de forma uniforme debido a las propiedades de la columna de distribución, email.

  1. El número de direcciones de correo electrónico era mayor o igual que el número de particiones.
  2. El número de filas por dirección de correo electrónico era similar (en nuestro caso, exactamente una fila por dirección porque hemos declarado correo electrónico como una clave).

Si se elige alguna tabla y columna de distribución en la que se produzca un error en cualquiera de las propiedades terminará con un tamaño de datos desigual en los trabajos, es decir, se producirá un sesgo en los datos.

Adición de restricciones a los datos distribuidos

El uso de Azure Cosmos DB for PostgreSQL le permite seguir disfrutando de la seguridad de una base de datos relacional, incluidas las restricciones de la base de datos. aunque con una limitación: Debido a la naturaleza de los sistemas distribuidos, Azure Cosmos DB for PostgreSQL no hará referencia cruzada a las restricciones de unicidad ni a la integridad referencial entre los nodos de trabajo.

Veamos el ejemplo de tabla users con una tabla 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');

Por motivos de eficacia, books se distribuye de la misma forma que users: por dirección de correo electrónico del propietario. La distribución de valores de columna similares se denomina coubicación.

No tuvimos ningún problema al distribuir books con una clave externa a los usuarios, ya que la clave estaba en una columna de distribución. Sin embargo, aparecerían problemas para hacer que isbn fuera una clave:

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

En una tabla distribuida, lo mejor es que las columnas sean un módulo único de la columna de distribución:

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

La restricción anterior simplemente hace que el valor de isbn sea único por usuario. Otra opción consiste en que books sea una tabla de referencia, en lugar de una tabla distribuida, y crear una tabla distribuida independiente que asocie books con users.

Consulta de tablas distribuidas

En las secciones anteriores, se ha visto la forma en que las filas de una tabla distribuida se colocan en particiones en los nodos de trabajo. La mayoría de las veces no es preciso saber cómo ni dónde se almacenan los datos en un clúster. Azure Cosmos DB for PostgreSQL tiene un ejecutor de consultas distribuidas que divide automáticamente las consultas SQL normales. Los ejecuta en paralelo en los nodos de trabajo cercanos a los datos.

Por ejemplo, podemos ejecutar una consulta para buscar la edad media de los usuarios y tratar la tabla users distribuida como una tabla normal en el coordinador.

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

query going to shards via coordinator

En segundo plano, el ejecutor de Azure Cosmos DB for PostgreSQL crea una consulta independiente para cada partición, las ejecuta en los trabajos y combina el resultado. Puede verlo si usa el comando EXPLAIN de 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)

La salida muestra un ejemplo de un plan de ejecución para un fragmento de la consulta que se ejecuta en la partición 102040 (la tabla users_102040 en el host 10.0.0.21 de trabajo). Los restantes fragmentos no se muestran porque son similares. Se puede ver que el nodo de trabajo examina las tablas de particiones y aplica el agregado. El nodo de coordinación combina los agregados para obtener resultado final.

Pasos siguientes

En este tutorial, ha creado una tabla distribuida y ha obtenido información sobre sus particiones y ubicaciones. Se ha visto el desafío de usar las restricciones de unicidad y una clave externa y, por último, se ha visto el funcionamiento de las consultas distribuidas en un nivel alto.