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
.