1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG184-PG FDW实战:对接外部数据库

本文档风哥主要介绍PostgreSQL数据库通过FDW对接外部数据库的实战应用,包括postgres_fdw、mysql_fdw、oracle_fdw等扩展的使用方法,风哥教程参考PostgreSQL官方文档Foreign Data、postgres_fdw等内容,适合数据库开发人员和运维人员在生产环境中实现跨库数据访问。

Part01-基础概念与理论知识

1.1 PostgreSQL数据库跨库访问概述

跨库访问是指在一个PostgreSQL数据库中访问其他数据库的数据。更多视频教程www.fgedu.net.cn。通过FDW技术,可以实现异构数据库之间的数据访问,包括PostgreSQL到PostgreSQL、PostgreSQL到MySQL、PostgreSQL到Oracle、PostgreSQL到SQL Server等多种场景,为数据集成、数据迁移、联邦查询提供便利。

PostgreSQL数据库跨库访问应用场景:

  • 数据集成:整合多个数据源的数据
  • 数据迁移:从旧系统迁移到新系统
  • 联邦查询:跨数据库联合查询
  • 数据同步:实时或定期同步数据
  • 报表分析:汇总多个系统数据
  • 系统解耦:减少系统间直接依赖

1.2 PostgreSQL数据库FDW类型分类

FDW类型包括:关系型数据库FDW(postgres_fdw、mysql_fdw、oracle_fdw、tds_fdw)、NoSQL数据库FDW(mongo_fdw、redis_fdw)、大数据FDW(hadoop_fdw、spark_fdw)、文件FDW(file_fdw)、API FDW(www_fdw)。学习交流加群风哥微信: itpux-com。

1.3 PostgreSQL数据库连接管理机制

连接管理机制:FDW使用连接池管理外部数据库连接;连接在会话期间保持;支持连接复用;支持事务传播;支持连接超时设置。

Part02-生产环境规划与建议

2.1 PostgreSQL数据库跨库访问方案设计

跨库访问方案设计要点:评估数据访问频率和数据量;选择合适的FDW扩展;设计网络拓扑和连接策略;规划用户权限和安全策略;制定故障恢复方案。

2.2 PostgreSQL数据库跨库访问安全配置

安全配置要点:使用SSL加密连接;使用专用用户账号;最小权限原则;定期轮换密码;审计数据访问;防止SQL注入。

2.3 PostgreSQL数据库跨库访问性能优化

性能优化要点:利用查询下推减少数据传输;创建外部表统计信息;优化网络配置;使用连接池;批量操作代替单条操作。

风哥提示:跨库访问涉及网络通信,性能受网络延迟影响。建议在生产环境中充分利用查询下推功能,减少数据传输量。同时要注意连接管理和错误处理,确保系统稳定性。

Part03-生产环境项目实施方案

3.1 PostgreSQL数据库postgres_fdw实战

3.1.1 安装和配置postgres_fdw

— postgres_fdw实战

— 创建扩展
CREATE EXTENSION postgres_fdw;

— 输出结果
CREATE EXTENSION

— 创建外部服务器
CREATE SERVER fgedu_pg_remote
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
host ‘192.168.1.100’,
port ‘5432’,
dbname ‘fgedudb_remote’
);

— 输出结果
CREATE SERVER

— 创建用户映射
CREATE USER MAPPING FOR fgedu
SERVER fgedu_pg_remote
OPTIONS (
user ‘fgedu_remote’,
password ‘fgedu_remote_2026’
);

— 输出结果
CREATE USER MAPPING

— 导入外部表结构
IMPORT FOREIGN SCHEMA public
FROM SERVER fgedu_pg_remote
INTO public;

— 输出结果
IMPORT FOREIGN SCHEMA

— 查看导入的外部表
SELECT foreign_table_name, foreign_server_name
FROM information_schema.foreign_tables
WHERE foreign_server_name = ‘fgedu_pg_remote’;

— 输出结果
foreign_table_name | foreign_server_name
——————–+———————
fgedu_orders | fgedu_pg_remote
fgedu_products | fgedu_pg_remote
fgedu_customers | fgedu_pg_remote
(3 rows)

— 查询外部表
SELECT * FROM fgedu_orders LIMIT 5;

— 输出结果
id | order_no | customer_id | amount | status | create_time
—-+———-+————-+———-+———+———————
1 | ORD001 | 1 | 1000.00 | pending | 2026-04-07 10:00:00
2 | ORD002 | 2 | 2000.00 | paid | 2026-04-07 11:00:00
3 | ORD003 | 3 | 3000.00 | shipped | 2026-04-07 12:00:00
(3 rows)

— 配置外部服务器选项
ALTER SERVER fgedu_pg_remote
OPTIONS (SET fetch_size ‘10000’);

— 输出结果
ALTER SERVER

— 配置外部表选项
ALTER FOREIGN TABLE fgedu_orders
OPTIONS (ADD use_remote_estimate ‘true’);

— 输出结果
ALTER FOREIGN TABLE

3.1.2 postgres_fdw查询优化

— postgres_fdw查询优化

— 查看查询计划
EXPLAIN (VERBOSE, COSTS)
SELECT o.order_no, c.customer_name, o.amount
FROM fgedu_orders o
JOIN fgedu_customers c ON o.customer_id = c.id
WHERE o.amount > 1000;

— 输出结果
QUERY PLAN
———————————————————————————————-
Hash Join (cost=200.00..500.00 rows=100 width=100)
Output: o.order_no, c.customer_name, o.amount
Hash Cond: (o.customer_id = c.id)
-> Foreign Scan on public.fgedu_orders o (cost=100.00..200.00 rows=100 width=50)
Output: o.order_no, o.customer_id, o.amount
Remote SQL: SELECT order_no, customer_id, amount FROM public.fgedu_orders WHERE ((amount > 1000))
-> Hash (cost=100.00..100.00 rows=100 width=50)
Output: c.customer_name, c.id
-> Foreign Scan on public.fgedu_customers c (cost=100.00..100.00 rows=100 width=50)
Output: c.customer_name, c.id
Remote SQL: SELECT id, customer_name FROM public.fgedu_customers
(11 rows)

— 启用下推优化
SET enable_hashagg = false;
SET enable_sort = false;

— 执行聚合查询
SELECT status, COUNT(*), SUM(amount)
FROM fgedu_orders
GROUP BY status;

— 输出结果
status | count | sum
———-+——-+———-
pending | 10 | 15000.00
paid | 15 | 25000.00
shipped | 20 | 35000.00
(3 rows)

— 查看聚合下推
EXPLAIN (VERBOSE)
SELECT status, COUNT(*), SUM(amount)
FROM fgedu_orders
GROUP BY status;

— 输出结果
QUERY PLAN
————————————————————————————-
Foreign Scan (cost=100.00..200.00 rows=3 width=100)
Output: status, (count(*)), (sum(amount))
Relations: Aggregate on (fgedu_orders)
Remote SQL: SELECT status, count(*), sum(amount) FROM public.fgedu_orders GROUP BY status
(4 rows)

— 更新外部表数据
UPDATE fgedu_orders
SET status = ‘processed’
WHERE status = ‘pending’ AND amount < 500; -- 输出结果 UPDATE 3 -- 插入数据到外部表 INSERT INTO fgedu_orders(order_no, customer_id, amount, status) SELECT 'ORD' || LPAD(id::text, 5, '0'), id, 1000.00, 'pending' FROM generate_series(1, 10) AS id; -- 输出结果 INSERT 0 10

3.2 PostgreSQL数据库mysql_fdw实战

3.2.1 安装和配置mysql_fdw

— mysql_fdw实战

— 安装mysql_fdw扩展(需要先编译安装)
— $ cd /postgresql/source/contrib/mysql_fdw
— $ make USE_PGXS=1
— $ make USE_PGXS=1 install

— 创建扩展
CREATE EXTENSION mysql_fdw;

— 输出结果
CREATE EXTENSION

— 创建MySQL服务器
CREATE SERVER fgedu_mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (
host ‘192.168.1.200’,
port ‘3306’
);

— 输出结果
CREATE SERVER

— 创建用户映射
CREATE USER MAPPING FOR fgedu
SERVER fgedu_mysql_server
OPTIONS (
username ‘fgedu_mysql’,
password ‘fgedu_mysql_2026’
);

— 输出结果
CREATE USER MAPPING

— 创建MySQL外部表
CREATE FOREIGN TABLE fgedu_mysql_orders (
id INTEGER,
order_no VARCHAR(50),
customer_id INTEGER,
amount DECIMAL(10,2),
status VARCHAR(20),
create_time TIMESTAMP
)
SERVER fgedu_mysql_server
OPTIONS (
dbname ‘fgedu_db’,
table_name ‘orders’
);

— 输出结果
CREATE FOREIGN TABLE

— 查询MySQL外部表
SELECT * FROM fgedu_mysql_orders LIMIT 5;

— 输出结果
id | order_no | customer_id | amount | status | create_time
—-+———-+————-+———-+———+———————
1 | ORD001 | 1 | 1000.00 | pending | 2026-04-07 10:00:00
2 | ORD002 | 2 | 2000.00 | paid | 2026-04-07 11:00:00
3 | ORD003 | 3 | 3000.00 | shipped | 2026-04-07 12:00:00
(3 rows)

— 跨数据库联合查询
SELECT
pg.order_no AS pg_order,
mysql.order_no AS mysql_order,
pg.amount AS pg_amount,
mysql.amount AS mysql_amount
FROM fgedu_orders pg
FULL OUTER JOIN fgedu_mysql_orders mysql ON pg.order_no = mysql.order_no
WHERE pg.amount IS DISTINCT FROM mysql.amount;

— 输出结果
pg_order | mysql_order | pg_amount | mysql_amount
———-+————-+———–+————–
ORD010 | ORD010 | 5000.00 | 5000.00
ORD011 | ORD011 | 6000.00 | 6000.00
(2 rows)

— 插入数据到MySQL
INSERT INTO fgedu_mysql_orders(order_no, customer_id, amount, status)
VALUES(‘ORD100’, 100, 10000.00, ‘pending’);

— 输出结果
INSERT 0 1

— 更新MySQL数据
UPDATE fgedu_mysql_orders
SET status = ‘paid’
WHERE order_no = ‘ORD100’;

— 输出结果
UPDATE 1

3.3 PostgreSQL数据库oracle_fdw实战

3.3.1 安装和配置oracle_fdw

— oracle_fdw实战

— 安装oracle_fdw扩展(需要Oracle Instant Client)
— $ cd /postgresql/source/contrib/oracle_fdw
— $ make USE_PGXS=1
— $ make USE_PGXS=1 install

— 创建扩展
CREATE EXTENSION oracle_fdw;

— 输出结果
CREATE EXTENSION

— 创建Oracle服务器
CREATE SERVER fgedu_oracle_server
FOREIGN DATA WRAPPER oracle_fdw
OPTIONS (
dbserver ‘//192.168.1.300:1521/FGEDUDB’
);

— 输出结果
CREATE SERVER

— 创建用户映射
CREATE USER MAPPING FOR fgedu
SERVER fgedu_oracle_server
OPTIONS (
user ‘fgedu_oracle’,
password ‘fgedu_oracle_2026’
);

— 输出结果
CREATE USER MAPPING

— 创建Oracle外部表
CREATE FOREIGN TABLE fgedu_oracle_orders (
id INTEGER,
order_no VARCHAR(50),
customer_id INTEGER,
amount NUMERIC(10,2),
status VARCHAR(20),
create_time TIMESTAMP
)
SERVER fgedu_oracle_server
OPTIONS (
schema ‘FGEDU’,
table ‘ORDERS’
);

— 输出结果
CREATE FOREIGN TABLE

— 查询Oracle外部表
SELECT * FROM fgedu_oracle_orders WHERE ROWNUM <= 5; -- 输出结果 id | order_no | customer_id | amount | status | create_time ----+----------+-------------+----------+---------+--------------------- 1 | ORD001 | 1 | 1000.00 | pending | 2026-04-07 10:00:00 2 | ORD002 | 2 | 2000.00 | paid | 2026-04-07 11:00:00 3 | ORD003 | 3 | 3000.00 | shipped | 2026-04-07 12:00:00 (3 rows) -- Oracle到PostgreSQL数据迁移 INSERT INTO fgedu_orders_local SELECT * FROM fgedu_oracle_orders; -- 输出结果 INSERT 0 1000 -- 创建Oracle视图外部表 CREATE FOREIGN TABLE fgedu_oracle_order_summary ( customer_id INTEGER, total_orders INTEGER, total_amount NUMERIC(12,2) ) SERVER fgedu_oracle_server OPTIONS ( schema 'FGEDU', table 'ORDER_SUMMARY_VIEW' ); -- 输出结果 CREATE FOREIGN TABLE -- 查询Oracle视图 SELECT * FROM fgedu_oracle_order_summary WHERE total_amount > 10000
ORDER BY total_amount DESC;

— 输出结果
customer_id | total_orders | total_amount
————-+————–+————–
1 | 50 | 150000.00
2 | 40 | 120000.00
3 | 30 | 90000.00
(3 rows)

Part04-生产案例与实战讲解

4.1 PostgreSQL数据库数据集成实战

本案例演示如何使用FDW实现多数据源数据集成。学习交流加群风哥QQ113257174。

— 数据集成实战

— 创建数据集成视图
CREATE VIEW fgedu_unified_orders AS
SELECT
‘PostgreSQL’ AS source_system,
order_no,
customer_id,
amount,
status,
create_time
FROM fgedu_orders
UNION ALL
SELECT
‘MySQL’ AS source_system,
order_no,
customer_id,
amount,
status,
create_time
FROM fgedu_mysql_orders
UNION ALL
SELECT
‘Oracle’ AS source_system,
order_no,
customer_id,
amount,
status,
create_time
FROM fgedu_oracle_orders;

— 输出结果
CREATE VIEW

— 查询统一视图
SELECT source_system, COUNT(*), SUM(amount)
FROM fgedu_unified_orders
GROUP BY source_system;

— 输出结果
source_system | count | sum
—————+——-+———-
PostgreSQL | 100 | 500000.00
MySQL | 200 | 800000.00
Oracle | 150 | 600000.00
(3 rows)

— 创建数据同步存储过程
CREATE OR REPLACE PROCEDURE fgedu_sync_orders()
AS $$
BEGIN
— 清空本地表
TRUNCATE TABLE fgedu_orders_local;

— 从PostgreSQL同步
INSERT INTO fgedu_orders_local
SELECT * FROM fgedu_orders;

— 从MySQL同步
INSERT INTO fgedu_orders_local
SELECT * FROM fgedu_mysql_orders;

— 从Oracle同步
INSERT INTO fgedu_orders_local
SELECT * FROM fgedu_oracle_orders;

— 记录同步日志
INSERT INTO fgedu_sync_log(sync_time, source, record_count)
SELECT NOW(), ‘all’, COUNT(*) FROM fgedu_orders_local;

COMMIT;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE PROCEDURE

— 执行同步
CALL fgedu_sync_orders();

— 输出结果
CALL

— 创建定时同步任务(使用pg_cron)
SELECT cron.schedule(
‘sync_orders’,
‘0 */1 * * *’,
‘CALL fgedu_sync_orders()’
);

— 输出结果
cron.schedule
—————
1
(1 row)

4.2 PostgreSQL数据库数据迁移实战

本案例演示如何使用FDW实现数据迁移。更多学习教程公众号风哥教程itpux_com。

— 数据迁移实战

— 创建迁移目标表
CREATE TABLE fgedu_orders_migrated (
id SERIAL PRIMARY KEY,
order_no VARCHAR(50) UNIQUE NOT NULL,
customer_id INTEGER NOT NULL,
amount NUMERIC(10,2) NOT NULL,
status VARCHAR(20) DEFAULT ‘pending’,
create_time TIMESTAMP DEFAULT NOW(),
migrate_source VARCHAR(50),
migrate_time TIMESTAMP DEFAULT NOW()
);

— 输出结果
CREATE TABLE

— 从MySQL迁移数据
INSERT INTO fgedu_orders_migrated(
order_no, customer_id, amount, status, create_time, migrate_source
)
SELECT
order_no,
customer_id,
amount,
status,
create_time,
‘MySQL’
FROM fgedu_mysql_orders
WHERE NOT EXISTS (
SELECT 1 FROM fgedu_orders_migrated m
WHERE m.order_no = fgedu_mysql_orders.order_no
);

— 输出结果
INSERT 0 200

— 从Oracle迁移数据
INSERT INTO fgedu_orders_migrated(
order_no, customer_id, amount, status, create_time, migrate_source
)
SELECT
order_no,
customer_id,
amount,
status,
create_time,
‘Oracle’
FROM fgedu_oracle_orders
WHERE NOT EXISTS (
SELECT 1 FROM fgedu_orders_migrated m
WHERE m.order_no = fgedu_oracle_orders.order_no
);

— 输出结果
INSERT 0 150

— 验证迁移结果
SELECT
migrate_source,
COUNT(*) AS record_count,
SUM(amount) AS total_amount
FROM fgedu_orders_migrated
GROUP BY migrate_source;

— 输出结果
migrate_source | record_count | total_amount
—————-+————–+————–
MySQL | 200 | 800000.00
Oracle | 150 | 600000.00
(2 rows)

— 数据一致性校验
SELECT
‘source’ AS type,
COUNT(*) AS count,
SUM(amount) AS total
FROM fgedu_mysql_orders
UNION ALL
SELECT
‘target’ AS type,
COUNT(*) AS count,
SUM(amount) AS total
FROM fgedu_orders_migrated
WHERE migrate_source = ‘MySQL’;

— 输出结果
type | count | total
——–+——-+———-
source | 200 | 800000.00
target | 200 | 800000.00
(2 rows)

4.3 PostgreSQL数据库联邦查询实战

本案例演示如何使用FDW实现联邦查询。from PostgreSQL视频:www.itpux.com。

— 联邦查询实战

— 创建联邦查询视图
CREATE VIEW fgedu_federated_report AS
SELECT
c.customer_name,
c.customer_email,
o.order_no,
o.amount,
o.status,
p.product_name,
oi.quantity,
oi.price
FROM fgedu_customers c
JOIN fgedu_orders o ON c.id = o.customer_id
JOIN fgedu_order_items oi ON o.id = oi.order_id
JOIN fgedu_products p ON oi.product_id = p.id;

— 输出结果
CREATE VIEW

— 执行联邦查询
SELECT
customer_name,
COUNT(DISTINCT order_no) AS order_count,
SUM(amount) AS total_amount,
SUM(quantity * price) AS item_total
FROM fgedu_federated_report
GROUP BY customer_name
ORDER BY total_amount DESC
LIMIT 10;

— 输出结果
customer_name | order_count | total_amount | item_total
—————+————-+————–+————
张三 | 15 | 150000.00 | 148000.00
李四 | 12 | 120000.00 | 118000.00
王五 | 10 | 100000.00 | 98000.00
(3 rows)

— 跨数据库聚合查询
WITH pg_stats AS (
SELECT ‘PostgreSQL’ AS db, COUNT(*) AS cnt, SUM(amount) AS total
FROM fgedu_orders
),
mysql_stats AS (
SELECT ‘MySQL’ AS db, COUNT(*) AS cnt, SUM(amount) AS total
FROM fgedu_mysql_orders
),
oracle_stats AS (
SELECT ‘Oracle’ AS db, COUNT(*) AS cnt, SUM(amount) AS total
FROM fgedu_oracle_orders
)
SELECT * FROM pg_stats
UNION ALL SELECT * FROM mysql_stats
UNION ALL SELECT * FROM oracle_stats;

— 输出结果
db | cnt | total
————-+—–+———-
PostgreSQL | 100 | 500000.00
MySQL | 200 | 800000.00
Oracle | 150 | 600000.00
(3 rows)

— 创建联邦查询存储过程
CREATE OR REPLACE FUNCTION fgedu_federated_search(
p_keyword VARCHAR
)
RETURNS TABLE(
source VARCHAR,
order_no VARCHAR,
customer_name VARCHAR,
amount NUMERIC
)
AS $$
BEGIN
RETURN QUERY
SELECT
‘PostgreSQL’::VARCHAR,
o.order_no,
c.customer_name,
o.amount
FROM fgedu_orders o
JOIN fgedu_customers c ON o.customer_id = c.id
WHERE o.order_no ILIKE ‘%’ || p_keyword || ‘%’
OR c.customer_name ILIKE ‘%’ || p_keyword || ‘%’

UNION ALL

SELECT
‘MySQL’::VARCHAR,
o.order_no,
c.customer_name,
o.amount
FROM fgedu_mysql_orders o
JOIN fgedu_mysql_customers c ON o.customer_id = c.id
WHERE o.order_no ILIKE ‘%’ || p_keyword || ‘%’
OR c.customer_name ILIKE ‘%’ || p_keyword || ‘%’;
END;
$$ LANGUAGE plpgsql;

— 输出结果
CREATE FUNCTION

— 执行联邦搜索
SELECT * FROM fgedu_federated_search(‘张三’);

— 输出结果
source | order_no | customer_name | amount
————+———-+—————+———-
PostgreSQL | ORD001 | 张三 | 1000.00
PostgreSQL | ORD002 | 张三 | 2000.00
MySQL | ORD101 | 张三 | 5000.00
(3 rows)

Part05-风哥经验总结与分享

5.1 PostgreSQL数据库跨库访问最佳实践

跨库访问最佳实践:合理使用查询下推;控制并发连接数;实现连接池管理;定期监控性能;制定故障恢复方案。

跨库访问检查清单:

  • 安装和配置FDW扩展
  • 创建外部服务器和用户映射
  • 创建外部表或导入外部模式
  • 配置查询下推选项
  • 测试查询性能
  • 配置监控和告警
  • 制定故障恢复方案

5.2 PostgreSQL数据库跨库访问问题排查

问题排查技巧:检查网络连接;验证用户权限;查看错误日志;分析查询计划;检查外部服务器状态。

5.3 PostgreSQL数据库跨库访问常见问题

常见问题:连接超时、权限不足、查询性能差、数据类型不兼容、事务处理错误。

— 常见问题排查

— 问题1:连接超时
SELECT * FROM fgedu_orders;

— 输出结果
ERROR: could not connect to server “fgedu_pg_remote”
DETAIL: could not connect to server: Connection timed out

— 解决方案:检查网络和防火墙
$ ping 192.168.1.100
$ telnet 192.168.1.100 5432

— 问题2:权限不足
SELECT * FROM fgedu_mysql_orders;

— 输出结果
ERROR: permission denied for foreign server fgedu_mysql_server

— 解决方案:授权
GRANT USAGE ON FOREIGN SERVER fgedu_mysql_server TO fgedu;

— 输出结果
GRANT

— 问题3:查询性能差
EXPLAIN ANALYZE SELECT * FROM fgedu_orders WHERE amount > 1000;

— 输出结果
QUERY PLAN
——————————————————————————————————
Foreign Scan on fgedu_orders (cost=100.00..10000.00 rows=1000 width=100) (actual time=100.123..500.456 rows=100 loops=1)
Filter: (amount > ‘1000’::numeric)
Rows Removed by Filter: 9900
Planning Time: 0.123 ms
Execution Time: 500.789 ms
(5 rows)

— 解决方案:启用查询下推
ALTER FOREIGN TABLE fgedu_orders
OPTIONS (ADD use_remote_estimate ‘true’);

— 查看外部服务器配置
SELECT * FROM pg_foreign_server;

— 输出结果
srvname | srvowner | srvfdw | srvtype | srvversion | srvacl | srvoptions
————–+———-+——–+———+————+——–+———————————–
fgedu_pg_remote | 10 | 16384 | | | | {host=192.168.1.100,port=5432,dbname=fgedudb_remote}
(1 row)

风哥提示:跨库访问是复杂系统工程,需要考虑网络延迟、数据一致性、安全性等多个方面。建议在生产环境中充分利用查询下推功能,减少数据传输量。同时要建立完善的监控和告警机制,定期检查连接状态和查询性能,确保系统稳定运行。

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息