3.4. Подключение и работа с кластером Shardman #

3.4.1. SQL
3.4.1.1. Отображение списка глобальных таблиц
3.4.1.2. Отображение списка сегментированная таблиц
3.4.1.3. Отображение списка глобальных последовательностей
3.4.1.4. Вычисление номера сегмента по ключу сегментирования
3.4.1.5. Расположение секций сегментированных таблиц по сегментам
3.4.1.6. Сбор статистики
3.4.2. psql/libpq
3.4.3. Python
3.4.4. Java
3.4.5. Go

Как уже упоминалось в Подразделе 3.1.2, кластер состоит из четырёх сегментов. Распределение секций с данными главной сегментированной таблицы по сегментам кластера будет выглядеть как показано ниже.

Для ключа сегментирования ticket_no:

Для ключа сегментирования book_ref:

Ниже приведены примеры для ключа сегментирования 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.