В первых двух статьях цикла о разделении таблиц мы рассмотрели, как возможности PostgreSQL по обрезке и обслуживанию разделов могут ускорить и облегчить разработку наших приложений. В этой заключительной статье цикла мы поэкспериментируем с функцией георазметки таблиц, которая автоматически распределяет данные приложения по нескольким местам.
Мы продолжим на примере крупной сети пиццерий. Эта сеть пиццерий имеет филиалы в Нью-Йорке, Лондоне и Гонконге. Она также использует один централизованный кластер базы данных для отслеживания заказов своих восхищенных клиентов.
На этот раз геораспределенный кластер баз данных работает на YugabyteDB. Для тех, кто еще не знаком с этой базой данных, YugabyteDB — это PostgreSQL-совместимая распределенная база данных, которая равномерно распределяет данные по кластеру узлов. Кроме того, она масштабирует операции чтения и записи, используя все ресурсы кластера. YugabyteDB поддерживает несколько многорегиональных развертываний, однако в этой статье мы остановимся на варианте с георазделами.
Таблица с георазметкой
Давайте снова возьмем таблицу PizzaOrders
, но теперь разделим ее по столбцу Region
. Таблица отслеживает ход выполнения заказа (представленного в первой статье), а вновь добавленный столбец Region
определяет местоположение заказа:
Как видите, разделить PizzaOrders
на разделы, которые будут распределены базой данных по удаленным географическим точкам, несложно. Но прежде чем мы перейдем к пошаговым инструкциям, давайте приведем некоторые обоснования для такого типа разбиения:
- Это хорошо для производительности и удобства пользователей — скорость и отзывчивость вашего приложения для пиццы будет одинаковой для всех клиентов, независимо от их местонахождения. Например, заказы из Гонконга будут проходить через таблицу
Orders_APAC
, где данные хранятся на узлах базы данных в APAC. - Это хорошо для регулирования данных — все заказы и персональные данные европейских клиентов не покинут пределов ЕС. Данные, принадлежащие разделу
Orders_EU
, будут находиться на узлах базы данных в Европе, что удовлетворяет требованиям GDPR. - Это хорошо с точки зрения бизнеса — головной офис сети пиццерий имеет полный контроль над единым кластером баз данных, который можно масштабировать в определенных географических регионах при увеличении нагрузки. Кроме того, приложение может легко запрашивать и соединять геораспределенные данные через единую конечную точку базы данных.
Запуск геораспределенного кластера
Теперь давайте поэкспериментируем с геораспределенными таблицами. Сначала разверните экземпляр YugabyteDB с геораспределением. Здесь у вас есть два варианта.
Вариант №1: Вы можете развернуть и настроить кластер с географическими разделами через интерфейс YugabyteDB Managed:
Вариант №2: Вы можете смоделировать кластер с географическим разделением на локальной машине с помощью YugabyteDB с открытым исходным кодом и Docker.
mkdir ~/yb_docker_data
docker network create yugabytedb_network
# Starting a node in the US
docker run -d --name yugabytedb_node_us --net yugabytedb_network -p 7001:7000 -p 9000:9000 -p 5433:5433
-v ~/yb_docker_data/node_us:/home/yugabyte/yb_data --restart unless-stopped
yugabytedb/yugabyte:latest bin/yugabyted start --listen=yugabytedb_node_us
--base_dir=/home/yugabyte/yb_data --daemon=false
--master_flags="placement_zone=A,placement_region=US,placement_cloud=CLOUD"
--tserver_flags="placement_zone=A,placement_region=US,placement_cloud=CLOUD"
# Starting a node in Europe
docker run -d --name yugabytedb_node_eu --net yugabytedb_network
-v ~/yb_docker_data/node_eu:/home/yugabyte/yb_data --restart unless-stopped
yugabytedb/yugabyte:latest bin/yugabyted start --join=yugabytedb_node_us --listen=yugabytedb_node_eu
--base_dir=/home/yugabyte/yb_data --daemon=false
--master_flags="placement_zone=A,placement_region=EU,placement_cloud=CLOUD"
--tserver_flags="placement_zone=A,placement_region=EU,placement_cloud=CLOUD"
# Starting a node in APAC
docker run -d --name yugabytedb_node_apac --net yugabytedb_network
-v ~/yb_docker_data/node_apac:/home/yugabyte/yb_data --restart unless-stopped
yugabytedb/yugabyte:latest bin/yugabyted start --join=yugabytedb_node_us --listen=yugabytedb_node_apac
--base_dir=/home/yugabyte/yb_data --daemon=false
--master_flags="placement_zone=A,placement_region=APAC,placement_cloud=CLOUD"
--tserver_flags="placement_zone=A,placement_region=APAC,placement_cloud=CLOUD"
# Updating the nodes’ placement
docker exec -i yugabytedb_node_us
yb-admin -master_addresses yugabytedb_node_us:7100,yugabytedb_node_eu:7100,yugabytedb_node_apac:7100
modify_placement_info CLOUD.US.A,CLOUD.EU.A,CLOUD.APAC.A 3
Мы будем использовать последний вариант, который запускает трехузловой кластер с одним узлом в каждой географической точке:
После запуска вы можете подключиться к экземпляру базы данных с помощью следующей команды psql:
psql -h 127.0.0.1 -p 5433 yugabyte -U yugabyte -w
Создание табличных пространств
Пространства таблиц — это удобная функция PostgreSQL. Она позволяет определить места в файловой системе, где хранятся файлы, представляющие объект базы данных. Будучи Postgres-совместимой базой данных, YugabyteDB поддерживает эту функцию и позволяет вам использовать пространства таблиц для гео-разметки.
Итак, следующим шагом будет создание табличных пространств для таблицы PizzaOrders
с географическим разделением:
CREATE TABLESPACE us_tablespace WITH (
replica_placement='{"num_replicas": 1, "placement_blocks":
[{"cloud":"CLOUD","region":"US","zone":"A","min_num_replicas":1}]}'
);
CREATE TABLESPACE eu_tablespace WITH (
replica_placement='{"num_replicas": 1, "placement_blocks":
[{"cloud":"CLOUD","region":"EU","zone":"A","min_num_replicas":1}]}'
);
CREATE TABLESPACE apac_tablespace WITH (
replica_placement='{"num_replicas": 1, "placement_blocks":
[{"cloud":"CLOUD","region":"APAC","zone":"A","min_num_replicas":1}]}'
);
Эти команды создают табличное пространство для каждого местоположения: США, ЕС и APAC. Каждая табличная область назначается узлу с аналогичной информацией о размещении. Например, us_tablespace
будет назначен узлу yugabytedb_node_us
, который вы запустили ранее, если информация о размещении этого узла (placement_zone=A,placement_region=US,placement_cloud=CLOUD
) соответствует размещению us_tablespace
.
Создание разделов
Последним шагом настройки является разбиение таблицы PizzaOrders
на три ранее рассмотренных раздела: Orders_US
, Orders_EU
и Orders_APAC
. Для этого можно воспользоваться командами, приведенными ниже:
CREATE TYPE status_t AS ENUM('ordered', 'baking', 'delivering', 'yummy-in-my-tummy');
CREATE TABLE PizzaOrders
(
order_id int,
order_status status_t,
order_time timestamp,
region text,
PRIMARY KEY (order_id, region)
) PARTITION BY LIST (region);
CREATE TABLE Orders_US
PARTITION OF PizzaOrders
FOR VALUES IN ('US') TABLESPACE us_tablespace;
CREATE TABLE Orders_EU
PARTITION OF PizzaOrders
FOR VALUES IN ('EU') TABLESPACE eu_tablespace;
CREATE TABLE Orders_APAC
PARTITION OF PizzaOrders
FOR VALUES IN ('APAC') TABLESPACE apac_tablespace;
На самом деле, комбинация нескольких возможностей позволяет осуществлять гео-разделение в YugabyteDB:
- Во-первых, исходная таблица (
PizzaOrders
) разбивается с помощью метода LIST Partitioning (PARTITION BY LIST (region)
). - Во-вторых, каждый раздел назначается одному из табличных пространств. Например, в приведенной выше команде раздел
Orders_APAC
назначается наTABLESPACE apac_tablespace
. - Наконец, каждая табличная область с ее разделами автоматически отображается на узлы YugabyteDB из соответствующей географии (т.е. размещается на них).
Хорошо, теперь давайте выполним эту команду, чтобы убедиться, что таблица PizzaOrders
действительно была разбита правильно:
d+ PizzaOrders;
Partitioned table "public.pizzaorders"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------+-----------------------------+-----------+----------+---------+----------+--------------+-------------
order_id | integer | | not null | | plain | |
order_status | status_t | | | | plain | |
order_time | timestamp without time zone | | | | plain | |
region | text | | not null | | extended | |
Partition key: LIST (region)
Indexes:
"pizzaorders_pkey" PRIMARY KEY, lsm (order_id HASH, region ASC)
Partitions: orders_apac FOR VALUES IN ('APAC'),
orders_eu FOR VALUES IN ('EU'),
orders_us FOR VALUES IN ('US')
Тестирование георазметки таблицы
Теперь вы готовы к последнему тесту. Продолжайте и добавьте несколько заказов в базу данных. На данный момент поместите некоторые данные в сеть пиццерий, расположенных в США:
INSERT INTO PizzaOrders VALUES
(1, 'yummy-in-my-tummy', '2021-12-27 22:00:00', 'US'),
(2, 'yummy-in-my-tummy', '2022-05-15 13:00:00', 'US'),
(6, 'baking', '2022-06-24 8:45:00', 'US'),
(7, 'baking', '2022-06-24 9:00:00', 'US');
Дважды проверьте, что данные попали в раздел Orders_US
(обратитесь к столбцу tableoid
в результате):
SELECT tableoid::regclass,* from PizzaOrders
ORDER BY order_id;
tableoid | order_id | order_status | order_time | region
-----------+----------+-------------------+---------------------+--------
orders_us | 1 | yummy-in-my-tummy | 2021-12-27 22:00:00 | US
orders_us | 2 | yummy-in-my-tummy | 2022-05-15 13:00:00 | US
orders_us | 6 | baking | 2022-06-24 08:45:00 | US
orders_us | 7 | baking | 2022-06-24 09:00:00 | US
Далее попытайтесь разместить заказы пиццы но клиентам из Лондона (регион ЕС) и Гонконга (регион АТР):
INSERT INTO PizzaOrders VALUES
(3, 'yummy-in-my-tummy', '2022-05-23 10:00:00', 'EU'),
(4, 'yummy-in-my-tummy', '2022-06-23 19:00:00', 'APAC'),
(5, 'delivering', '2022-06-24 8:30:00', 'APAC'),
(8, 'ordered', '2022-06-24 10:00:00', 'EU');
ERROR: Illegal state: Nonlocal tablet accessed in local transaction: tablet 49fb2ab17298424096d215f5f1f32515
Если вы следовали этим инструкциям, вы получите приведенное выше сообщение об ошибке. Это происходит потому, что наша psql-сессия открыта через узел YugabyteDB, развернутый в США (yugabytedb_node_us
). А по умолчанию YugabyteDB не позволяет выполнять транзакции, которые охватывают несколько географических регионов. Итак, какие у вас есть варианты? Вы можете подключиться к узлам в ЕС и АТР и вставлять данные оттуда. Или вы можете включить опцию force_global_transaction
и вставить данные с узла, расположенного в США:
SET force_global_transaction = TRUE;
INSERT INTO PizzaOrders VALUES
(3, 'yummy-in-my-tummy', '2022-05-23 10:00:00', 'EU'),
(4, 'yummy-in-my-tummy', '2022-06-23 19:00:00', 'APAC'),
(5, 'delivering', '2022-06-24 8:30:00', 'APAC'),
(8, 'ordered', '2022-06-24 10:00:00', 'EU');
После успешного выполнения команды убедитесь, что заказы размещены правильно по разделам и соответствующим географическим регионам (снова обратитесь к столбцу tableoid
в выводе):
SELECT tableoid::regclass,* from PizzaOrders
ORDER BY order_id;
tableoid | order_id | order_status | order_time | region
-------------+----------+-------------------+---------------------+--------
orders_us | 1 | yummy-in-my-tummy | 2021-12-27 22:00:00 | US
orders_us | 2 | yummy-in-my-tummy | 2022-05-15 13:00:00 | US
orders_eu | 3 | yummy-in-my-tummy | 2022-05-23 10:00:00 | EU
orders_apac | 4 | yummy-in-my-tummy | 2022-06-23 19:00:00 | APAC
orders_apac | 5 | delivering | 2022-06-24 08:30:00 | APAC
orders_us | 6 | baking | 2022-06-24 08:45:00 | US
orders_us | 7 | baking | 2022-06-24 09:00:00 | US
orders_eu | 8 | ordered | 2022-06-24 10:00:00 | EU
Подведение итогов…
Итак, этого более чем достаточно для начала, когда речь заходит о георазметке таблиц. Если вы хотите узнать, как добавить новые регионы в уже существующий кластер с георазметкой или как противостоять сбоям на уровне регионов, прочтите эту статью.
Эта статья завершает наш цикл статей, посвященный теме разделения таблиц для разработчиков приложений. Получайте удовольствие от создания этих приложений и следите за новыми материалами, связанными с базами данных, распределенными системами и Java!