Поделиться через


Руководство. Сегментирование данных на рабочих узлах в Azure Cosmos DB для PostgreSQL

Область применения: Azure Cosmos DB для PostgreSQL (на базе расширения базы данных Citus до PostgreSQL)

В этом руководстве вы узнаете, как использовать Azure Cosmos DB для PostgreSQL:

  • создание сегментов с хэш-распределением;
  • определение расположения сегментов таблицы;
  • определение смещения распределения;
  • создание ограничения для распределенных таблиц;
  • выполнение запросов к распределенным данным.

Необходимые компоненты

В этом руководстве требуется запущенный кластер с двумя рабочими узлами. Если у вас нет работающего кластера, следуйте руководству по созданию кластера и вернитесь к этому.

Данные с хэш-распределением

Распределение строк таблицы между несколькими серверами PostgreSQL — это ключевой способ масштабирования запросов в Azure Cosmos DB для PostgreSQL. Вместе несколько узлов могут содержать больше данных, чем традиционная база данных, а во многих случаях для выполнения запросов могут параллельно использоваться ЦП рабочих узлов. Концепция хэш-распределенных таблиц также называется сегментированием на основе строк.

В разделе предварительных требований мы создали кластер с двумя рабочими узлами.

Координатор и два рабочих узла

Таблицы метаданных в узле координатора отслеживают рабочие узлы и распределенные данные. Активные рабочие узлы можно просмотреть в таблице pg_dist_node.

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

Примечание.

Имена узлов в Azure Cosmos DB для PostgreSQL — это внутренние IP-адреса в виртуальной сети, а фактические адреса, которые вы видите, могут отличаться.

Строки, сегменты и размещение

Чтобы использовать ресурсы ЦП и хранилища рабочих узлов, необходимо распределить данные таблицы по всему кластеру. С помощью распределения таблицы каждая строка назначается логической группе, которая называется сегментом. Создайте таблицу и распределите ее:

-- 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 для PostgreSQL назначает каждую строку сегменту в зависимости от значения столбца распространения, который, в нашем случае, мы указали email. Каждая строка будет располагаться точно в одном сегменте, а каждый сегмент может содержать несколько строк.

Таблица

По умолчанию create_distributed_table() включает 32 сегмента, как можно увидеть, выполнив подсчет в таблице метаданных pg_dist_shard:

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

Azure Cosmos DB для PostgreSQL использует pg_dist_shard таблицу для назначения строк сегментам на основе хэша значения в столбце распространения. Для работы с этим руководством не нужно вникать в детали хэширования. Важнее то, что мы можем отправить запрос и увидеть, какие значения сопоставлены с определенными идентификаторами сегментов:

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

Сопоставление строк сегментам выполняется абсолютно логично. Сегменты должны быть назначены определенным рабочим узлам для хранения, в том, что Azure Cosmos DB для PostgreSQL вызывает размещение сегментов.

Сегменты, назначенные рабочим узлам

Мы можем рассмотреть размещение сегментов в pg_dist_placement. Если соединить эту таблицу с другими таблицами метаданных, можно понять, где расположен каждый сегмент.

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

Неравномерное распределение данных

Кластер работает наиболее эффективно при равномерном размещении данных на рабочих узлах, а также при совместном размещении связанных данных на одних и том же рабочих узлах. В рамках этого раздела мы уделим внимание первому вопросу — однородности размещения.

Для демонстрации создадим пример данных для таблицы 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);

Чтобы определить размеры сегментов, можно выполнить для сегментов функции для определения размера таблицы.

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

Как мы видим, сегменты имеют одинаковый размер. Размещения распределены равномерно между рабочими узлами, поэтому можно сделать вывод, что рабочие узлы содержат приблизительно одинаковое число строк.

Строки в нашем примере users распределены равномерно благодаря свойствам столбца распределения email.

  1. Число адресов электронной почты было больше числа сегментов или равно ему.
  2. Число строк на адрес электронной почты было похожим (в нашем случае использовалась точно одна строка на адрес, так как мы объявили его ключом).

Если выбрать таблицу и столбец распределения таким образом, что одно из свойств не будет работать, размещение данных в рабочих узлах будет неравномерным, то есть возникнет неравномерное распределение данных.

Добавление ограничений к распределенным данным

Использование Azure Cosmos DB для PostgreSQL позволяет продолжать пользоваться безопасностью реляционной базы данных, включая ограничения базы данных. Но здесь нужно помнить об одном моменте. Из-за характера распределенных систем Azure Cosmos DB для PostgreSQL не будет перекрестно ссылаться на ограничения уникальности или ссылочной целостности между рабочими узлами.

Рассмотрим наш пример таблицы users со связанной таблицей.

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

Для повышенной эффективности мы распределим books так же, как users: по адресу электронной почты владельца. Распределение по аналогичным значениям столбцов называется совместным размещением.

У нас не возникло проблем с распределением пользователям таблицы books с внешним ключом, так как ключ был доступен в столбце распределения. Но если мы попытаемся сделать из isbn ключ, возникнут сложности:

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

В распределенной таблице оптимальным методом будет сделать столбцы уникальным остатком от деления столбца распределения:

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

Приведенное выше ограничение всего лишь делает isbn уникальным для пользователя. Другой вариант — сделать таблицу books ссылочной таблицей (а не распределенной) и создать отдельную распределенную таблицу, связывающую данные о книгах с данными о пользователях.

Запрашивание распределенных таблиц

Из предыдущих разделов мы узнали, как строки распределенной таблицы размещаются в сегментах в рабочих узлах. Большую часть времени вам не нужно знать, как или где хранятся данные в кластере. Azure Cosmos DB для PostgreSQL имеет распределенный исполнитель запросов, который автоматически разбивает регулярные запросы SQL. Он запускает их параллельно в рабочих узлах, расположенных близко к данным.

Например, можно выполнить запрос, чтобы найти средний возраст пользователей, обработав распределенную таблицу users как обычную таблицу в координаторе.

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

Запрос к сегментам через координатор

За кулисами исполнитель Azure Cosmos DB для PostgreSQL создает отдельный запрос для каждого сегмента, запускает их на рабочих местах и объединяет результат. Это можно увидеть, если воспользоваться командой EXPLAIN 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)

В выходных данных приведен пример плана выполнения для фрагмента запроса, выполняющегося в сегменте 102040 (таблица users_102040 в рабочем узле 10.0.0.21). Другие фрагменты не отображаются, так как они похожи. Как мы видим, рабочий узел проверяет таблицы сегмента и выполняет статистические вычисления. Узел координатора объединяет агрегированные данные для получения окончательного результата.

Следующие шаги

С помощью инструкций из этого руководства мы создали распределенную таблицу. В нем также описаны сегменты и размещения. Мы рассмотрели проблему уникальности и ограничения для внешних ключей, а также то, как распределенные запросы работают на верхнем уровне.