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.