Postgres, как настроить запросы между разными БД

Всем привет. На днях пришлось вспомнить магию Postgres, задача была решена, по результатам написал инструкцию в корпоративную базу знаний, чтобы в следующий раз не тратить время на. Думаю на Хабре от тоже будет польза.

Ниже речь будет идти о чтении данных одной БД из другой БД. В частности я решал такую проблему:

нашей Системе данные о пользователях записаны в одной БД, а данные об их торгах в другой, без дополнительных настроек Postgres не позволяет использовать в одном запросе данные из разных БД.

То есть запрос вида:

select a.id from auth.public.user a join trade.public.tenders t on a.id = t.user_id;

Вызовет ошибку "[0A000] ERROR: cross-database references are not implemented".

Что делать ?

Необходимо создать обёртку для внешней таблицы, о том как это сделать читайте ниже.

Настраиваем внешнюю БД

Подключаемся к СУБД под супер пользователем postgres, подключаемся к БД, которая является внешней по отношению к БД из которой будем делать запрос.

docker exec -it postgres psql -U postgres
\c trade

Создаём нужный нам источник данных, в нашем случае это представление

create or replace view tender_users AS
select    t.id_user
from    tender t        
group by    id_user
order by id_user
;

Даём права на созданный источник (да, права тотально на всё, у нас пока так можно)

GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA public TO trade;

Кроме того, это нелогичная команда, потому что она нужна даже если мы создаём представления под пользователем trade, но без неё не работает, выдаёт ошибку не достаточно прав (когда делаем запрос через FOREIGN DATA WRAPPER).

Настраиваем собственную БД

Переключаемся на БД из которой будем делать запросы

\c prod

Подключаем расширение для запросов во внешнюю БД

create extension postgres_fdw;

Проверяем что расширение включилось

\dx

Должны увидеть:

                               List of installed extensions
     Name     | Version |   Schema   |                    Description
--------------+---------+------------+----------------------------------------------------
 plpgsql      | 1.0     | pg_catalog | PL/pgSQL procedural language
 postgres_fdw | 1.0     | public     | foreign-data wrapper for remote PostgreSQL servers
(2 rows)

Нам нужна вторая строка, первая - "приблудная".

Создаём внешний сервер

CREATE SERVER tenders FOREIGN DATA WRAPPER postgres_fdw  OPTIONS (dbname 'trade', host 'postgres', port '5432');

Выдаём права на использование расширения и сервера

GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw to prod;
GRANT USAGE ON FOREIGN SERVER tenders TO prod;

Теперь самое главное, задаём учётные данные для подключения к внешней БД

CREATE USER MAPPING for prod SERVER tenders OPTIONS (user 'trade', password '***');

Подключаем источник данных

Теперь можно в ручную создать внешние таблицы, можно автоматически. Как создать источник данных вручную можно почитать по ссылкам из списка литературы в конце статьи. Если автоматически, то:

IMPORT FOREIGN SCHEMA public LIMIT TO (tender_users) FROM SERVER tenders INTO public;

Проверяем, что данные доступны для чтения

prod=# select count(*) from tender_users;
 count
-------
     0
(1 row)

Ошибок чтения нет, значит связь успешно установлена. Теперь можно использовать данные внешней таблицы в запросах к нашим внутренним таблицам.

Успех !

Postscriptum

Последний раз я это делал 5 лет назад, и возможно мои знания устарели, как эту проблему решают в актуальных версиях Postgres ? напишите в комментариях.

Версия СУБД

prod=# select version();
                                                    version
---------------------------------------------------------------------------------------------------------------
 PostgreSQL 13.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 13.2.1_git20240309) 13.2.1 20240309, 64-bit
(1 row)

Список литературы

@SbWereWolf
13.10.2024 01:55 UTC
Первоисточник

Комментарии

@codanych
13.10.2024 04:53 UTC
0

Привет, классная статья, спасибо, что напомнили мне о магии Postgres! 😊

Что вы думаете о dblink в современных версиях? Может быть, он тоже будет работать? Мне также интересно, как FDW работает у вас в продакшене - есть ли подводные камни?

Еще раз спасибо!

@ky0
13.10.2024 05:49 UTC
0

У Коли Постгрес в Докере, а вы такие сложные вопросы задаёте :)

13.10.2024 07:25 UTC
+2

Если посмотреть какие статьи Коля когда то писал, то можно забрать свои слова обратно.

Докер для настольного макетирования удобная платформа. На мой вкус. С вашими вкусами спорить не буду.

13.10.2024 07:28 UTC
0

Так вы макетируете или пишете (более-менее) универсальную инструкцию?

13.10.2024 09:10 UTC
+3

я не пишу в своей инструкции что СУБД должна быть в контейнере, я пишу подключаемся к СУБД. продолжайте свои нападки они всех раздражают возбуждают

13.10.2024 11:29 UTC
+1

Что не так с postgres в docker?

@NickNal
13.10.2024 06:33 UTC
+1

У postgres_fdw есть куча настроек для оптимизации DML-операций, которых нет в dblink. Зато через dblink можно делать то же самое + дёргать функции и процедуры, а в postgres_fdw это умышленно не добавляют. Ну и dblink лучше подходит для эмуляции автономных транзакций внутри одной БД

Хайлоад сделать на них не получится, но для вспомогательных операций типа логирования транзакций вполне пойдёт

18.10.2024 06:38 UTC
0

С хайдоад не согласен. Через dblink можно так распараллелить запрос, как родному планировщику и не снилось.

Да и сама возможность асинхронного выполнения запросов многого стоит.

@danolivo
13.10.2024 08:07 UTC
+1

С FDW стоит быть аккуратнее - учитывать преобразование дерево плана - запрос - новое дерево плана + всякие особенности навроде использования курсоров на удаленной стороне, что исключает параллельные планы. Ну и сама технология pushdown  тоже не все позволяет пушить.

В общем, если посмотреть на TPC-H и сравнить результаты выполнения запросов над локальными таблицами и таблицами, подключенными по FDW, то все достаточно печально получается.

@torbasow
13.10.2024 15:36 UTC
0
@vmalyutin
17.10.2024 03:59 UTC
+1

Открою Вам секрет. У PG ещё триллион расширений. А ещё они все документированы.

Если уж писать про расширение, то хотя бы упомянуть про его подводные камни. Где оно подойдёт, где не подойдет и почему.

Например, dblink и postgres_fdw хранят пароль в открытом виде. Прям killer фича