3.4. Подключение и работа с кластером Shardman
Как уже упоминалось в Раздел 3.1.2, кластер состоит из четырёх сегментов. Распределение секций с данными главной сегментированной таблицы по сегментам кластера будет выглядеть как показано ниже.
Для ключа сегментирования ticket_no:
tickets_0— сегмент-1 (узел кластераnode1)tickets_1— сегмент-2 (узел кластераnode2)tickets_2— сегмент-3 (узел кластераnode3)tickets_3— сегмент-4 (узел кластераnode4)
Для ключа сегментирования book_ref:
bookings_0— сегмент-1 (узел кластераnode1)bookings_1— сегмент-2 (узел кластераnode2)bookings_2— сегмент-3 (узел кластераnode3)bookings_3— сегмент-4 (узел кластераnode4)
Ниже приведены примеры для ключа сегментирования book_ref, но указанный в подразделах код будет справедлив и для ключа сегментирования ticket_no.
Не стоит рассматривать данный код как оптимальный или пригодный для производственной среды. Он дан только как пример реализации создания пула соединений для работы с кластером Shardman.
Общее для всех приведённых примеров — строка подключения к кластеру, которая должна содержать имена узлов, номера TCP-портов, имя и пароль пользователя, имя базы данных для подключения и набор параметров сеанса.
Такую строку можно получить с помощью утилиты shardmanctl. В простейшем случае строка будет выглядеть так:
$ shardmanctl getconnstr dbname=postgres host=node1,node2,node3,node4 port=5432,5432,5432,5432
Её можно использовать для подключения к узлам кластера или для создания пула соединений в приложениях.
3.4.1. SQL
В Shardman реализовано несколько удобных функций и представлений для наблюдения за кластером:
Вывод списка глобальных таблиц
Вывод списка сегментированная таблиц
Вывод списка глобальных последовательностей
Вычисление номера сегмента по ключу сегментирования
Выполнение
ANALYZEдля всех глобальных и сегментированных таблиц в кластере
3.4.1.1. Отображение списка глобальных таблиц
Для вывода всех глобальных таблиц в кластере используйте представление shardman.global_tables:
postgres=# select
relname as table_name,
nspname as schema
from shardman.global_tables;
table_name | schema
------------+----------
aircrafts | bookings
seats | bookings
airports | bookings
flights | bookings
(4 rows)3.4.1.2. Отображение списка сегментированная таблиц
Для получения сведений обо всех сегментированных таблицах в кластере выполните запрос к представлению shardman.sharded_tables как показано ниже:
postgres=# select
relname as table_name,
nparts as partitions,
colocated_with::oid::regclass::text as colocated_with,
nspname as schema
from shardman.sharded_tables;
table_name | partitions | colocated_with | schema
-----------------+------------+----------------+----------
bookings | 4 | | bookings
ticket_flights | 4 | bookings | bookings
tickets | 4 | bookings | bookings
boarding_passes | 4 | bookings | bookings
(4 rows)3.4.1.3. Отображение списка глобальных последовательностей
Для вывода всех глобальных последовательностей в кластере используйте представление shardman.sequence:
postgres=# select
seqns as schema,
seqname as sequence_name,
seqmin as min_value,
seqmax as max_value,
seqblk as bulk_size
from shardman.sequence;
schema | sequence_name | min_value | max_value | bulk_size
----------+-------------------------+-----------+---------------------+-----------
bookings | flights_flight_id_seq | 262145 | 9223372036854775807 | 65536
(1 rows)3.4.1.4. Вычисление номера сегмента по ключу сегментирования
Для вывода имени секции, содержащей данные и имя группы репликации вызовите функцию shardman.get_partition_for_value(). Например, пусть book_ref = 0369E5:
postgres=# select * from shardman.get_partition_for_value(
'bookings'::regclass,
'0369E5'::character(6));
rgid | local_nspname | local_relname | remote_nspname | remote_relname
------+---------------+---------------+----------------+----------------
1 | bookings | bookings_0 | bookings | bookings_0 Этот вывод показывает, что данные в секции bookings_0 таблицы bookings расположены в том же узле, в котором выполнялся запрос.
Выполните запрос, показывающий имя сервера, где находится секция с данными. Если подключиться к серверу, содержащему искомую секцию, то в выводе имени сервера будет написано «current server» (текущий сервер). Если данные расположены в другом сегменте, показывается имя узла мастера сегмента:
SELECT p.rgid,
local_relname AS partition_name,
CASE
WHEN r.srvid IS NULL THEN 'current server'
ELSE (SELECT (SELECT split_part(kv, '=', 2)
FROM (SELECT unnest(fs.srvoptions) as kv) x
WHERE split_part(kv, '=', 1) = 'host')
FROM shardman.repgroups rg
JOIN pg_catalog.pg_foreign_server AS fs ON fs.oid = rg.srvid
WHERE rg.id = p.rgid)
END AS server_name
FROM shardman.get_partition_for_value('bookings'::regclass, '0369E5'::character(6)) p
JOIN shardman.repgroups AS r ON
r.id = p.rgid;
rgid | partition_name | server_name
------+----------------+----------------
1 | bookings_0 | current server
(1 row)При выполнении запроса с другим ключом сегментирования — 0369E6 — результат будет таким:
rgid | partition_name | server_name
------+----------------+-------------
4 | bookings_3_fdw | node4
(1 row)
Видно, что секция находится на узле node4.
Также обратите внимание, что параметр shardman.rgid позволяет вам найти номер узла с сеансом подключения. Для этого выполните следующий запрос:
SELECT pg_catalog.current_setting('shardman.rgid');Это значение можно использовать для определения расположения секций в подобных запросах.
Функция shardman.get_partition_for_value() в основном предназначена для административных целей и лучшего понимания топологии данных.
Как правило, административные функции не следует использовать при написании SQL-кода для доступа к данным.
3.4.1.5. Расположение секций сегментированных таблиц по сегментам
Список всех сегментированных таблиц в схеме bookings, количество их секций и распределение по серверам (сегментам) можно получить из метаданных Shardman на любом узле кластера.
Рассмотрим следующий запрос:
SELECT p.rel::regclass::text AS table_name,
p.pnum,
p.rgid,
r.srvid,
fs.srvname
FROM shardman.parts p
JOIN shardman.repgroups r
ON p.rgid = r.id
LEFT OUTER JOIN pg_foreign_server fs
ON r.srvid = fs.oid;Чтобы выяснить как распределены данные, объедините этот запрос с подзапросом из Раздел 3.4.1.4:
SELECT p.rel::regclass AS table_name,
st.nparts AS total_parts,
p.pnum AS num_part,
CASE
WHEN r.srvid IS NULL THEN 'connected server'
ELSE
(SELECT split_part(kv, '=', 2)
FROM (SELECT unnest(fs.srvoptions) AS kv) x
WHERE split_part(kv, '=', 1) = 'host')
END AS server_name
FROM shardman.parts p
JOIN shardman.repgroups r
ON p.rgid = r.id
LEFT JOIN shardman.sharded_tables st
ON p.rel = st.rel
LEFT JOIN pg_foreign_server fs
ON r.srvid = fs.oid
WHERE st.nspname = 'bookings'
ORDER BY table_name, num_part, server_name;Формат вывода результата: имя таблицы, количество секций в таблице, номер секции и имя сервера:
table_name | total_parts | num_part | server_name
--------------------------+-------------+----------+------------------
bookings.bookings | 4 | 0 | connected server
bookings.bookings | 4 | 1 | node2
bookings.bookings | 4 | 2 | node3
bookings.bookings | 4 | 3 | node4
bookings.ticket_flights | 4 | 0 | connected server
bookings.ticket_flights | 4 | 1 | node2
bookings.ticket_flights | 4 | 2 | node3
bookings.ticket_flights | 4 | 3 | node4
bookings.tickets | 4 | 0 | connected server
bookings.tickets | 4 | 1 | node2
bookings.tickets | 4 | 2 | node3
bookings.tickets | 4 | 3 | node4
bookings.boarding_passes | 4 | 0 | connected server
bookings.boarding_passes | 4 | 1 | node2
bookings.boarding_passes | 4 | 2 | node3
bookings.boarding_passes | 4 | 3 | node4
3.4.1.6. Сбор статистики
Для сбора статистики по сегментированным и глобальным таблицам вызовите функцию shardman.global_analyze(). Сначала эта функция выполняет сбор статистики для всех локальных секций распределённых таблиц на каждом узле, а затем транслирует эту статистику на другие узлы. Для глобальной таблицы будет выполнен сбор статистики на одном выбранном узле, после чего собранная статистика транслируется на все остальные узлы кластера.
3.4.2. psql/libpq
Для подключения к кластеру Shardman и полноценной работы с ним, достаточно подключиться к любому узлу кластера. Для этого необходимо сначала получить строку подключения.
В документации PostgreSQL описана строка подключения к кластеру. Строка может быть представлена в двух вариантах: ключ-значение и URI. Для подключения к кластеру Shardman можно использовать любой из этих вариантов.
Кроме того, должны быть указаны некоторые параметры. Список этих параметров также доступен в документации PostgreSQL.
Для параметра target_session_attrs должно быть указано значение read-write, то есть приемлемы только подключения, допускающие транзакции чтения/записи. При успешном подключении к узлу кластера будет отправлен запрос «SHOW transaction_read_only;». Если в результате получено значение on, соединение будет закрыто. Если в строке подключения указаны несколько серверов, будут перебираться остальные серверы, как и при неудачной попытке подключения. Использование параметра target_session_attrs даёт возможность указывать в строке подключение как ведущие серверы, так и реплики кластера Shardman.
Ниже представлены примеры подключения:
psql -d "dbname=postgres host=node3,node4,node2,node1 port=5432,5432,5432,5432 user=username password=password target_session_attrs=read-write"
psql postgres://username:password@node1:5432,node2:5432,node3:5432,node4:5432/postgres?target_session_attrs=read-write
3.4.3. Python
Подключение к кластеру Shardman с использованием библиотеки psycopg2 будет выглядеть следующим образом:
import psycopg2
from psycopg2 import pool
pool = psycopg2.pool.SimpleConnectionPool(
min_size=1,
max_size=5,
user="pguser",
password="*****",
host="node1,node2,node3,node4",
port="5432,5432,5432,5432",
database="postgres",
target_session_attrs="read-write")
connection = pool.getconn() Создаётся пул соединений со следующими параметрами: минимальное количество подключений min_size=1, а максимальное — max_size=5. Далее выбирается подключение для соединения с кластером, указывается логин и пароль пользователя, список узлов, TCP-портов, база данных и параметры подключения (за дополнительной информацией обратитесь к Раздел 3.4.2)
3.4.4. Java
Подключение к кластеру Shardman с использованием JDBC будет выглядеть следующим образом:
String url = "jdbc:postgresql://node1:5432,node2:5432,node3:5432,node4:5432/postgres?loadBalanceHosts=true&targetServerType=primary";
Properties props = new Properties();
props.setProperty("user","postgres");
props.setProperty("password","********");
Connection conn = DriverManager.getConnection(url, props); Здесь в переменной url содержится строка подключения, где перечислены все доступные в кластере ведущие серверы сегментов. Если не указывать дополнительные параметры подключения драйвера JDBC, то подключение к кластеру будет происходить через первый доступный для соединения узел. Данный вариант не всегда может оказаться удобным. Поэтому, в строке подключения добавлены параметры, позволяющие использовать разные сегменты кластера для разных подключений.
Параметр loadBalanceHosts=true позволяет перебирать узлы, подключаясь к какому-то одному из них, а параметр targetServerType=primary указывает, что необходимо выбирать только ведущие сервера, тогда в строку подключения можно добавлять реплики.
3.4.5. Go
Подключение к кластеру Shardman для Go не слишком отличается от способов подключения, принятых в Java или Python. Необходимо использовать перечисление узлов, их TCP-портов, параметры подключения и выбрать подходящий драйвер.
Одним из таких драйверов для Go является pgx версии 4 или 5.
Ниже представлен пример строки подключения и создания пула для подключения к кластеру:
dbURL := "postgres://username:password@node1:5432,node2:5432,node3:5432,node4:5432/postgres?target_session_attrs=read-write") dbPool, err := pgxpool.New(context.Background(), dbURL)
Рекомендуем также ознакомиться с описанием параметра target_session_attrs.