Поделиться через


Совместное размещение таблиц в Azure Cosmos DB для PostgreSQL

Область применения: Azure Cosmos DB для PostgreSQL (на базе расширения базы данных Citus до PostgreSQL)

Совместное размещение — это хранение взаимосвязанных данных вместе на одних и тех же узлах. Когда все необходимые данные доступны без дополнительных сетевых запросов, то запросы выполняются быстрее. Совместное размещение взаимосвязанных данных на разных узлах позволяет эффективно выполнять запросы параллельно на каждом узле.

Совместное размещение данных для таблиц с хэш-распределением

В Azure Cosmos DB для PostgreSQL строка хранится в сегменте, если хэш значения в столбце распределения попадает в хэш-диапазон сегментов. Сегменты с одинаковым диапазоном хэша всегда размещены на одном узле. Строки с одинаковыми значениями столбца распределения всегда находятся на одном узле в разных таблицах. Концепция хэш-распределенных таблиц также называется сегментированием на основе строк. В сегментации на основе схемы таблицы в распределенной схеме всегда размещаются совместно.

На схеме показаны сегменты с одинаковым хэш-диапазоном, размещенные на одном узле для сегментов событий и сегментов страниц.

Практичный пример совместного размещения

Рассмотрим следующие таблицы, которые могут быть частью мультитенантной веб-аналитики SaaS:

CREATE TABLE event (
  tenant_id int,
  event_id bigint,
  page_id int,
  payload jsonb,
  primary key (tenant_id, event_id)
);

CREATE TABLE page (
  tenant_id int,
  page_id int,
  path text,
  primary key (tenant_id, page_id)
);

Теперь мы хотим ответить на запросы, которые могут выдаваться на панели мониторинга для клиента. Пример запроса: "Возврат количества посещений за прошлую неделю для всех страниц, начинающихся с /blog, в клиенте 6".

Если наши данные находились на одном сервере PostgreSQL, можно легко выразить запрос с помощью полнофункциональных операций, предлагаемых SQL:

SELECT page_id, count(event_id)
FROM
  page
LEFT JOIN  (
  SELECT * FROM event
  WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

Пока рабочий набор для этого запроса умещается в памяти, подходящим решением является таблица с одним сервером. Рассмотрим возможности масштабирования модели данных с помощью Azure Cosmos DB для PostgreSQL.

Распределение таблиц по идентификатору

Запросы с одним сервером начинают замедляться по мере роста количества клиентов и данных, хранимых для каждого клиента. Рабочий набор больше не помещается в памяти, а ЦП становится узким местом.

В этом случае можно сегментировать данные по многим узлам с помощью Azure Cosmos DB для PostgreSQL. Самое важное при сегментировании — выбрать столбец распределения. Начнем с упрощенного выбора использования event_id для таблицы событий и page_id для таблицы page:

-- naively use event_id and page_id as distribution columns

SELECT create_distributed_table('event', 'event_id');
SELECT create_distributed_table('page', 'page_id');

Если данные распределены между разными рабочими ролями, мы не сможем выполнить соединение, как в одном узле PostgreSQL. Вместо этого необходимо выполнить два запроса.

-- (Q1) get the relevant page_ids
SELECT page_id FROM page WHERE path LIKE '/blog%' AND tenant_id = 6;

-- (Q2) get the counts
SELECT page_id, count(*) AS count
FROM event
WHERE page_id IN (/*…page IDs from first query…*/)
  AND tenant_id = 6
  AND (payload->>'time')::date >= now() - interval '1 week'
GROUP BY page_id ORDER BY count DESC LIMIT 10;

После этого приложение должно объединить результаты выполнения двух шагов.

Процессу выполнения запросов необходимо обращаться к данным в сегментах, разбросанных по узлам.

На схеме показан неэффективный подход с использованием нескольких запросов к таблицам событий и страниц в двух узлах.

В этом случае распределение данных создает существенные недостатки.

  • Дополнительные расходы на отправку запросов к каждому сегменту и выполнение нескольких запросов.
  • Дополнительные расходы на Q1, возвращающий множество строк клиенту.
  • Q2 становится большим.
  • Если необходимо записать запросы в несколько шагов, приложение нужно будет изменить.

Данные распределены, поэтому запросы можно выполнить параллельно. Это полезно только в том случае, если объем работы, выполняемой запросом, значительно превышает издержки, связанные с запросом к многим сегментам.

Распределение таблиц по клиентам

В Azure Cosmos DB для PostgreSQL строки с одинаковым значением столбца распространения гарантированно находятся на одном узле. Начиная заново, мы можем создавать таблицы с параметром tenant_id в качестве столбца распределения.

-- co-locate tables by using a common distribution column
SELECT create_distributed_table('event', 'tenant_id');
SELECT create_distributed_table('page', 'tenant_id', colocate_with => 'event');

Теперь Azure Cosmos DB для PostgreSQL может ответить на исходный запрос с одним сервером без изменений (Q1):

SELECT page_id, count(event_id)
FROM
  page
LEFT JOIN  (
  SELECT * FROM event
  WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

Из-за фильтрации и присоединения к tenant_id Azure Cosmos DB для PostgreSQL знает, что весь запрос можно ответить с помощью набора совместно размещенных сегментов, содержащих данные для этого конкретного клиента. Один узел PostgreSQL может ответить на запрос за один шаг.

На схеме показан один запрос к одному узлу, что является более эффективным подходом.

В некоторых случаях необходимо изменить запросы и схемы таблиц, добавив идентификатор клиента к уникальным ограничениям и условиям соединения. Это изменение обычно является простым.

Следующие шаги

  • Узнайте, как данные клиента совместно отображаются в мультитенантном руководстве.