在多数据源并存的企业环境中,常常需要在不同数据库之间进行联合分析或数据迁移。PostgreSQL 作为功能强大的开源关系型数据库,提供了 Foreign Data Wrapper(FDW,外部数据包装器)机制,允许它像访问本地表一样查询远程数据库。
本文将手把手带你配置 mysql_fdw,实现 PostgreSQL 对 MySQL 表的透明读写访问,真正做到"一处查询,跨库联动"。
一、什么是 mysql_fdw?
mysql_fdw 是一个 PostgreSQL 的 FDW 扩展,由 EnterpriseDB 开发并开源。它通过 MySQL 客户端库(libmysqlclient)连接远程 MySQL 实例,并将远程表映射为 PostgreSQL 中的"外部表"(Foreign Table)。你可以在 PostgreSQL 中直接对这些外部表执行 SELECT、INSERT、UPDATE、DELETE 等操作(取决于权限和配置)。
✅ 适用场景:
- 实时报表聚合(PG + MySQL 联合查询)
- 数据迁移过渡期
- 微服务间临时数据打通
- 避免 ETL 中间层,简化架构
二、环境准备
前提条件
- PostgreSQL 10+(推荐 12+)
- MySQL 5.7 或 8.0
- 操作系统:Linux(本文以 Ubuntu 22.04 为例)
- 具备 sudo 权限
安装依赖
# 安装编译工具和 PostgreSQL 开发包
sudo apt update
sudo apt install build-essential postgresql-server-dev-all libmysqlclient-dev git
# 克隆 mysql_fdw 源码(官方 GitHub)
git clone https://github.com/EnterpriseDB/mysql_fdw.git
cd mysql_fdw
⚠️ 注意:确保
libmysqlclient-dev版本与目标 MySQL 兼容。若使用 MySQL 8.0,可能需额外处理认证插件(如 caching_sha2_password)。
三、编译并安装 mysql_fdw
# 编译(自动检测 pg_config)
make
# 安装到 PostgreSQL 扩展目录
sudo make install
验证是否安装成功:
# 查看 PostgreSQL 的 extension 目录
pg_config --sharedir
# 应能在 $SHAREDIR/extension/ 下看到 mysql_fdw.control 和 .so 文件
四、在 PostgreSQL 中启用 mysql_fdw
以 postgres 用户登录 psql:
-- 创建扩展(每个需使用的数据库都要执行)
CREATE EXTENSION mysql_fdw;
五、配置外部服务器与用户映射
1. 创建外部服务器(Foreign Server)
CREATE SERVER mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (
host '192.168.1.100', -- MySQL 主机 IP
port '3306' -- MySQL 端口
);
2. 创建用户映射(User Mapping)
将 PostgreSQL 用户映射到 MySQL 的认证凭据:
CREATE USER MAPPING FOR postgres -- PostgreSQL 本地用户
SERVER mysql_server
OPTIONS (
username 'remote_user',
password 'secure_password'
);
🔒 安全建议:避免在 SQL 中明文写密码,可结合
.pgpass或 Vault 等密钥管理工具。
六、创建外部表(Foreign Table)
假设 MySQL 中有数据库 sales_db,表 orders 结构如下:
-- MySQL 表结构示例
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_name VARCHAR(100),
amount DECIMAL(10,2),
created_at DATETIME
);
在 PostgreSQL 中创建对应的外部表:
CREATE FOREIGN TABLE foreign_orders (
id INTEGER,
customer_name TEXT,
amount NUMERIC(10,2),
created_at TIMESTAMP
)
SERVER mysql_server
OPTIONS (
dbname 'sales_db',
table_name 'orders'
);
📌 注意:
- 字段名必须一致(大小写敏感)
- 类型需兼容(MySQL 的 VARCHAR → PG 的 TEXT,DATETIME → TIMESTAMP)
- 不支持所有 MySQL 特有类型(如 JSON 需测试)
七、实战查询与写入
查询数据
SELECT * FROM foreign_orders WHERE amount > 1000;
联合本地表查询
SELECT u.name, o.amount
FROM local_users u
JOIN foreign_orders o ON u.mysql_order_id = o.id;
写入操作(需 MySQL 用户有写权限)
INSERT INTO foreign_orders (id, customer_name, amount, created_at)
VALUES (1001, 'Alice', 1500.00, NOW());
UPDATE foreign_orders SET amount = 1600 WHERE id = 1001;
DELETE FROM foreign_orders WHERE id = 1001;
⚠️ 警告:写操作会直接修改 MySQL 数据,请谨慎使用!
八、常见问题与排查
1. 连接失败:could not connect to MySQL
- 检查 MySQL 是否允许远程连接(
bind-address) - 确认防火墙开放 3306 端口
- 验证 MySQL 用户权限:
GRANT SELECT, INSERT... ON sales_db.* TO 'remote_user'@'%'
2. 认证失败(MySQL 8.0)
MySQL 8 默认使用 caching_sha2_password,而旧版 libmysqlclient 可能不支持。
解决方案:
-
升级
libmysqlclient-dev到 8.0+ -
或在 MySQL 中创建兼容用户:
CREATE USER 'remote_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
3. 性能问题
- 外部表查询无法使用 PostgreSQL 的索引优化
- 复杂 JOIN 可能导致大量数据拉取
- 建议:对高频查询结果物化(Materialized View)或定期同步
九、替代方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| mysql_fdw | 实时、SQL 透明、支持读写 | 依赖 libmysqlclient,部署复杂 |
| 逻辑复制 + ETL | 稳定、可控 | 延迟高,需维护管道 |
| dblink(不支持 MySQL) | --- | PostgreSQL 原生 dblink 仅支持 PG |
十、总结
通过 mysql_fdw,PostgreSQL 成功打破了与 MySQL 的数据孤岛。虽然它不适合高并发写入或超大规模分析场景,但在开发调试、轻量级集成、临时数据桥接等场景中极具价值。