PostgreSQL 9.6(2016)开始支持并行查询、FDW 联邦查询;PG 10 引入了原生表分区、逻辑复制;PG 11/12/13/14/15/16/17 连续在 JSON 索引、SQL/JSON 标准、逻辑复制槽等方向稳步推进。2020 年后,pgvector(向量检索)、TimescaleDB(时序)、ParadeDB(全文检索)、pg_duckdb(OLAP) 让 PostgreSQL 成了"一个数据库覆盖 OLTP + OLAP + 向量 + 时序"的"多模态"基座。这篇文章把 PG 9.6/17.5 的 Docker 化、扩展生态、MySQL 迁移一次性收齐。
阅读对象:考虑从 MySQL 迁到 PostgreSQL 的团队、需要 AI 时代向量检索、需要时序与关系混合存储、对 ParadeDB / pg_duckdb 等新扩展感兴趣的同学。
覆盖范围:PostgreSQL 官方镜像 + TimescaleDB HA 镜像启动;All-in-one 扩展矩阵(pgvector / TimescaleDB / ParadeDB / pg_duckdb 4 大扩展的核心场景与价值);pgloader 从 MySQL 迁移到 PostgreSQL 的完整流程;序列重置、超表创建、文本字段扩容等实战。
一、PostgreSQL 在 Docker 里的关键决策
| 决策 | 选项 | 推荐 |
|---|
| 镜像 | 官方 postgres / timescale/timescaledb-ha / bitnami/postgresql | 时序选 TimescaleDB;其他用官方 |
| 数据目录 | /var/lib/postgresql/data | 默认值 |
| 高可用 | Patroni / 流复制 / 共享存储 | 单实例 Patroni 是主流 |
| 备份 | pg_dump / pg_basebackup / WAL-G | 大库 WAL-G 流式 |
| 迁移 | pgloader / 自研 ETL | pgloader 是事实标准 |
二、官方 postgres 镜像启动
2.1 拉取与启动
1
2
3
4
5
6
7
8
9
| docker pull postgres:17.5
docker pull postgres:9.6.21-alpine # 旧版兼容
docker run -d \
--name pg \
-p 5433:5432 \
-e POSTGRES_PASSWORD={{REDACTED}} \
-e PGDATA=/var/lib/postgresql/data/pgdata \
postgres:17.5
|
关键参数:
POSTGRES_PASSWORD:必填,超级用户 postgres 的密码PGDATA:自定义数据目录(避免与挂载点冲突)- 默认端口 5432,开发环境用
-p 5433:5432 区分多版本
2.2 TimescaleDB HA 镜像
Timescale 出品的 HA 镜像(基于 Patroni + etcd + PgBouncer):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| docker pull timescale/timescaledb-ha:pg18.4-ts2.27.1
# 镜像内已封装好
# 5432/tcp - PostgreSQL
# 8008/tcp - Patroni REST API
# 8081/tcp - PgBouncer / 监控
mkdir -p /home/docker/postgres/pgdata
chown -R 1000:1000 /home/docker/postgres/pgdata
docker run -d \
--name pg \
--restart=always \
-p 5432:5432 \
-e POSTGRES_DB=sonarqube \
-e POSTGRES_PASSWORD={{REDACTED}} \
-v /home/docker/postgres/pgdata:/home/postgres/pgdata \
timescale/timescaledb-ha:pg18.4-ts2.27.1
|
2.3 TimescaleDB 独立镜像
不带 HA 模式,更轻量:
1
2
3
4
5
6
7
8
9
10
11
| docker pull timescale/timescaledb:2.20.3-pg17
mkdir -p /home/docker/timescaledb/data
docker run -d --name timescaledb \
--restart=always \
-e TZ=Asia/Shanghai \
-e POSTGRES_PASSWORD={{REDACTED}} \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-p 5432:5432 \
-v /home/docker/timescaledb/data:/var/lib/postgresql/data \
timescale/timescaledb:2.20.3-pg17
|
三、All-in-one 扩展矩阵
PG 的真正威力在"一个数据库,多种能力"。下表是常见扩展的对比:
| 扩展 | 核心场景 | 核心价值 | 与 PG 整合方式 |
|---|
| ParadeDB | 全文检索 | 替代原生 tsvector/tsquery,提供更强大的全文检索(分词、排名、聚合) | 作为 PG 扩展安装,直接操作 PG 表 |
| pg_duckdb / pg_clickhouse | 离线 / 实时数据分析 | 复用 DuckDB/ClickHouse 的 OLAP 能力,无需迁移数据即可做高性能分析 | FDW 形式访问,或直接查询 PG 表 |
| pgvector | 向量搜索(AI 场景) | 存储向量、计算余弦/内积/欧式距离,支撑 AI 检索/推荐 | 扩展 PG 数据类型(vector)和索引(ivfflat/hnsw) |
| TimescaleDB | 时序数据存储与分析 | 对时序数据做分区、压缩、降采样,优化时序查询性能 | 扩展 PG 的表结构(超表)和时序函数 |
3.1 启用 TimescaleDB 扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| -- 进入 psql
docker exec -it timescaledb psql -U postgres
-- 创建 schema
CREATE SCHEMA mine;
-- 创建普通表(测点历史数据)
CREATE TABLE "mine"."wps_point_hdata" (
"id" bigserial NOT NULL,
"equip_id" int8 NOT NULL,
"equip_name" varchar(100) NOT NULL,
"point_id" int8 NOT NULL,
"point_value" numeric(50,2) NOT NULL,
"data_time" timestamptz(6) NOT NULL,
"rule_user_id" int8 NOT NULL,
PRIMARY KEY ("id", "data_time")
);
-- 转换为超表(hypertable)
SELECT create_hypertable('mine.wps_point_hdata', 'data_time');
|
**超表(hypertable)**是 TimescaleDB 的核心抽象:对用户看就是一张表,底层自动按时间分区(chunks),写入和查询都自动路由到对应分区。
3.2 启用 pgvector 扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id bigserial PRIMARY KEY,
content text,
embedding vector(1536) -- OpenAI ada-002 维度
);
-- 余弦相似度搜索(Top 5)
SELECT id, content, 1 - (embedding <=> '[0.1, 0.2, ...]'::vector) AS similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 5;
-- 创建 HNSW 索引(速度比 ivfflat 快 5-10x)
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
|
3.3 启用 pg_duckdb 做 OLAP
1
2
3
4
5
6
| CREATE EXTENSION pg_duckdb;
-- 直接用 DuckDB 引擎查询 PG 表
SELECT region, avg(price)
FROM duckdb.query('SELECT * FROM sales WHERE date > ''2024-01-01''')
GROUP BY region;
|
pg_duckdb 的杀手锏:无需 ETL,就能在生产 PG 表上跑 DuckDB 的向量化分析引擎。亿级数据秒级响应。
四、从 MySQL 迁移到 PostgreSQL(pgloader)
4.1 pgloader 安装
1
2
3
4
5
6
7
8
9
10
11
| # Debian/Ubuntu
apt update && apt install -y pgloader
pgloader --version
# 从源码(最新版)
wget https://github.com/dimitri/pgloader/releases/download/v3.6.9/pgloader-bundle-3.6.9.tgz
tar xvf pgloader-bundle-3.6.9.tgz
cd pgloader-bundle-3.6.9
make CL="sbcl --dynamic-space-size 9000" save
cp bin/pgloader /usr/bin/pgloader
chmod +x /usr/bin/pgloader
|
4.2 MySQL 端准备迁移账号
1
2
3
4
5
| -- 在 MySQL 中
CREATE USER 'pg_migrate'@'%' IDENTIFIED WITH mysql_native_password BY '{{REDACTED}}';
GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION CLIENT ON *.* TO 'pg_migrate'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'pg_migrate'@'%';
FLUSH PRIVILEGES;
|
防超时设置:
1
2
3
4
5
6
7
| SET GLOBAL net_read_timeout = 6000;
SET GLOBAL net_write_timeout = 6000;
SET GLOBAL max_allowed_packet = 128 * 1024 * 1024;
SET GLOBAL wait_timeout = 86400;
SET GLOBAL interactive_timeout = 86400;
SET GLOBAL tmp_table_size = 64 * 1024 * 1024;
SET GLOBAL max_heap_table_size = 64 * 1024 * 1024;
|
4.3 编写迁移配置 mysql2pg.load
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| LOAD DATABASE
FROM mysql://pg_migrate:{{REDACTED}}@mysql_host:3306/source_db
INTO postgresql://postgres:{{REDACTED}}@pg_host:5432/target_db
WITH include drop,
create tables,
create indexes,
reset sequences,
downcase identifiers,
workers = 2,
concurrency = 2,
batch rows = 100
CAST type json to jsonb drop default,
type datetime to timestamp
SET work_mem = '512MB', maintenance_work_mem = '2GB';
|
关键参数:
include drop:迁移前先 drop 目标表reset sequences:序列从当前最大值 +1 开始(避免主键冲突)downcase identifiers:表名/列名转小写(PG 默认区分大小写)CAST type json to jsonb:MySQL JSON → PG JSONB(更高效)batch rows = 100:每批 100 行(OOM 风险时调小)
4.4 执行迁移
1
2
3
4
5
6
7
8
9
| # 试运行(不实际写数据)
pgloader --dry-run mysql2pg.load
# 实际迁移(后台跑)
nohup pgloader mysql2pg.load > migration_log.log 2>&1 &
tail -f migration_log.log
# 大数据量增加 dynamic-space-size
pgloader --dynamic-space-size 9000 -vv mysql2pg.load
|
4.5 迁移后验证
1
2
3
4
5
6
7
| -- 对比记录数
SELECT 'mysql' AS src, count(*) FROM <via_fdw>
UNION ALL
SELECT 'pg' AS src, count(*) FROM <local_table>;
-- 重置序列(pgloader 的 reset sequences 选项会自动做,但保险起见手动)
SELECT setval('users_id_seq', (SELECT max(id) FROM users));
|
五、PG 9.6 / 17 / 18 选型速查
| 版本 | 关键特性 | 适合 |
|---|
| 9.6 (2016) | 并行查询、FDW、pg_basebackup | 遗留系统(已 EOL) |
| 14 (2021) | SQL/JSON 标准、性能大幅提升 | 主流稳定 |
| 15 (2022) | MERGE 语句、逻辑复制 | 新功能 |
| 16 (2023) | 性能优化、逻辑复制槽 | 高负载 |
| 17 (2024) | 增量备份、改进 EXPLAIN | 新项目首选 |
| 18 (2025) | 异步 I/O、UUIDv7 | 实验性 |
六、扩展阅读