2026 年了,直接用 PostgreSQL 吧

引言

想象一下你的数据库就像你的家。你的家有客厅、卧室、浴室、厨房和车库,每个房间都有不同的用途。但它们都在同一个屋檐下,通过走廊和门连接在一起。你不会仅仅因为需要做饭就建一个独立的餐厅大楼,也不会在城市另一端建一个商业车库只为停车。

这就是 PostgreSQL 的理念:一个有多个房间的家。搜索、向量、时序、队列——都在同一个屋檐下。

但这正是专业数据库厂商不希望你听到的。他们的市场团队花了数年时间说服你「为正确的工作使用正确的工具」。这听起来很合理,很明智。而且能卖出很多数据库。

让我告诉你为什么这是个陷阱,以及为什么在 99% 的情况下 PostgreSQL 才是更好的选择。

「使用正确工具」的陷阱

你听过这个建议:「为正确的工作使用正确的工具」

听起来很明智。于是你最终得到了:

  1. Elasticsearch - 用于搜索
  2. Pinecone - 用于向量搜索
  3. Redis - 用于缓存
  4. MongoDB - 用于文档存储
  5. Kafka - 用于消息队列
  6. InfluxDB - 用于时序数据
  7. PostgreSQL - 用于…剩下的东西

恭喜你。你现在有七个数据库要管理:

  • 七种查询语言要学习
  • 七种备份策略要维护
  • 七个安全模型要审计
  • 六套凭证要轮换
  • 七个监控面板要看
  • 七个可能在凌晨 3 点崩溃的东西

当某个东西真的崩溃时?祝你好运能快速启动一个测试环境来调试。

这里有个不同的想法:直接用 PostgreSQL

为什么现在这很重要:AI 时代

这不仅仅是为了简单性。AI Agent 让数据库蔓延变成了一场噩梦

想想 Agent 需要做什么:

  • 快速用生产数据启动测试数据库
  • 尝试修复或实验
  • 验证是否有效
  • 然后销毁

用一个数据库? 一条命令就够了。Fork、测试、完成。

用七个数据库? 现在你需要:

  • 协调 Postgres、Elasticsearch、Pinecone、Redis、MongoDB 和 Kafka 的快照
  • 确保它们都在同一个时间点
  • 启动七个不同的服务
  • 配置七个不同的连接字符串
  • 祈祷在测试时没有数据漂移
  • 完成后拆除七个服务

如果没有大量的研发投入,这几乎是不可能的。

而且不仅仅是 Agent。每次凌晨 3 点出问题时,你都需要启动一个测试环境来调试。六个数据库意味着协调噩梦。一个数据库,只需一条命令。

在 AI 时代,简单性不仅仅是优雅。它是必需品

「但专用数据库更好!」

让我们正面解决这个问题。

神话:专用数据库在特定任务上远远优于通用数据库。

现实:有时它们在狭窄的任务上略好一些。但它们也带来了不必要的复杂性。就像为每顿饭雇一个私人厨师。听起来很奢侈,但增加了费用、协调开销,并制造了你之前没有的问题。

事实是:99% 的公司不需要它们。排名前 1% 的公司拥有数千万用户和相匹配的庞大工程团队。你读过他们关于专用数据库 X 如何为他们工作的博客文章。但那是他们的规模、他们的团队、他们的问题。对其他人来说,PostgreSQL 绰绰有余。

大多数人没有意识到的是:PostgreSQL 扩展使用与专用数据库相同或更好的算法(在许多情况下)

「专用数据库」的溢价?主要是营销。

PostgreSQL vs 专用数据库算法对比

需求专用工具PostgreSQL 扩展算法相同?
全文搜索Elasticsearchpg_textsearch✅ 都使用 BM25
向量搜索Pineconepgvector + pgvectorscale✅ 都使用 HNSW/DiskANN
时序数据InfluxDBTimescaleDB✅ 都使用时间分区
缓存RedisUNLOGGED tables✅ 都使用内存存储
文档存储MongoDBJSONB✅ 都使用文档索引
地理空间专用 GISPostGIS✅ 自 2001 年以来的行业标准

这些不是打折版本。它们是相同/更好的算法,经过实战测试、开源,并且通常由相同的研究人员开发。

基准测试支持这一点:

  • pgvectorscale:在 99% 召回率下,延迟比 Pinecone 低 28 倍,成本降低 75%
  • TimescaleDB:匹配或超越 InfluxDB,同时提供完整的 SQL
  • pg_textsearch:与 Elasticsearch 相同的 BM25 排名算法

隐藏成本不断累积

除了 AI/Agent 问题,数据库蔓延还有复合成本:

任务一个数据库七个数据库
备份策略17
监控面板17
安全补丁17
值班手册17
故障转移测试17

认知负担:你的团队需要学习 SQL、Redis 命令、Elasticsearch Query DSL、MongoDB 聚合、Kafka 模式和 InfluxDB 的非原生 SQL 变通方法。这不是专业化,这是碎片化

数据一致性:让 Elasticsearch 与 Postgres 保持同步?你构建同步任务。它们失败了。数据漂移。你添加协调机制。那也失败了。现在你在维护基础设施而不是构建功能。

SLA 数学:三个系统各有 99.9% 的正常运行时间 = 99.7% 的组合正常运行时间。这意味着每年停机 26 小时而不是 8.7 小时。每个系统都会增加你的故障模式。

现代 PostgreSQL 技术栈

这些扩展并不新鲜。它们已经在生产环境中使用多年:

  • PostGIS:自 2001 年以来(24 年)。为 OpenStreetMap 和 Uber 提供支持
  • 全文搜索:自 2008 年以来(17 年)。内置于 Postgres 核心
  • JSONB:自 2014 年以来(11 年)。与 MongoDB 一样快,具有 ACID
  • TimescaleDB:自 2017 年以来(8 年)。21K+ GitHub stars
  • pgvector:自 2021 年以来(4 年)。19K+ GitHub stars

超过 48,000 家公司使用 PostgreSQL,包括 Netflix、Spotify、Uber、Reddit、Instagram 和 Discord。

AI 时代的扩展

AI 时代带来了新一代扩展:

扩展替代品亮点
pgvectorscalePinecone, QdrantDiskANN 算法。延迟降低 28 倍,成本降低 75%
pg_textsearchElasticsearch原生 BM25 排名
pgai外部 AI 管道数据变化时自动同步嵌入

这意味着什么:构建 RAG 应用过去需要 Postgres + Pinecone + Elasticsearch + 粘合代码。

现在?只需 PostgreSQL。一个数据库,一种查询语言,一次备份,一个 fork 命令让你的 AI Agent 启动测试环境。

快速开始:添加这些扩展

你只需要这些:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
-- 使用 BM25 的全文搜索
CREATE EXTENSION pg_textsearch;

-- AI 向量搜索
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale;

-- AI 嵌入和 RAG 工作流
CREATE EXTENSION ai;

-- 时序数据
CREATE EXTENSION timescaledb;

-- 消息队列
CREATE EXTENSION pgmq;

-- 定时任务
CREATE EXTENSION pg_cron;

-- 地理空间
CREATE EXTENSION postgis;

就是这样。

实战代码示例

以下是每个用例的实际示例。跳到你需要的部分。

全文搜索(替代 Elasticsearch)

扩展:pg_textsearch(真正的 BM25 排名)

你正在替代的:

  • Elasticsearch:独立的 JVM 集群、复杂映射、同步管道、Java 堆调优
  • Solr:同样的问题,不同的包装
  • Algolia:每 1000 次搜索 $1,外部 API 依赖

你得到的:为 Elasticsearch 提供支持的完全相同的 BM25 算法,直接在 Postgres 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
-- 创建表
CREATE TABLE articles (
  id SERIAL PRIMARY KEY,
  title TEXT,
  content TEXT
);

-- 创建 BM25 索引
CREATE INDEX idx_articles_bm25 ON articles 
USING bm25(content) WITH (text_config = 'english');

-- 使用 BM25 评分搜索
SELECT title, -(content <@> 'database optimization') as score
FROM articles
ORDER BY content <@> 'database optimization'
LIMIT 10;

混合搜索:在一个查询中结合 BM25 + 向量

1
2
3
4
5
6
7
8
9
SELECT 
  title,
  -(content <@> 'database optimization') as bm25_score,
  embedding <=> query_embedding as vector_distance,
  0.7 * (-(content <@> 'database optimization')) + 
  0.3 * (1 - (embedding <=> query_embedding)) as hybrid_score
FROM articles
ORDER BY hybrid_score DESC
LIMIT 10;

这是 Elasticsearch 需要单独插件才能完成的功能。在 Postgres 中,这只是 SQL。

向量搜索(替代 Pinecone)

扩展:pgvector + pgvectorscale

你正在替代的:

  • Pinecone:每月最低 $70、独立基础设施、数据同步难题
  • Qdrant、Milvus、Weaviate:更多需要管理的基础设施

你得到的:pgvectorscale 使用 DiskANN 算法(来自微软研究院),在 99% 召回率下实现了比 Pinecone 低 28 倍的 p95 延迟16 倍的吞吐量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
-- 启用扩展
CREATE EXTENSION vector;
CREATE EXTENSION vectorscale CASCADE;

-- 带嵌入的表
CREATE TABLE documents (
  id SERIAL PRIMARY KEY,
  content TEXT,
  embedding vector(1536)
);

-- 高性能索引(DiskANN)
CREATE INDEX idx_docs_embedding ON documents 
USING diskann(embedding);

-- 查找相似文档
SELECT content, embedding <=> '[0.1, 0.2, ...]'::vector as distance
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

使用 pgai 自动同步嵌入

1
2
3
4
5
6
7
8
SELECT ai.create_vectorizer(
  'documents'::regclass,
  loading => ai.loading_column(column_name=>'content'),
  embedding => ai.embedding_openai(
    model=>'text-embedding-3-small', 
    dimensions=>'1536'
  )
);

现在每次 INSERT/UPDATE 都会自动重新生成嵌入。没有同步任务,没有漂移,没有凌晨 3 点的故障。

时序数据(替代 InfluxDB)

扩展:TimescaleDB(21K+ GitHub stars)

你正在替代的:

  • InfluxDB:独立数据库、Flux 查询语言或非原生 SQL、有限的 SQL 支持
  • Prometheus:适合指标,但不适合应用数据

你得到的:自动时间分区、高达 90% 的压缩、连续聚合。完整的 SQL。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 启用 TimescaleDB
CREATE EXTENSION timescaledb;

-- 创建表
CREATE TABLE metrics (
  time TIMESTAMPTZ NOT NULL,
  device_id TEXT,
  temperature DOUBLE PRECISION
);

-- 转换为超表
SELECT create_hypertable('metrics', 'time');

-- 使用时间桶查询
SELECT 
  time_bucket('1 hour', time) as hour,
  AVG(temperature)
FROM metrics
WHERE time > NOW() - INTERVAL '24 hours'
GROUP BY hour;

-- 自动删除旧数据
SELECT add_retention_policy('metrics', INTERVAL '30 days');

-- 压缩(减少 90% 存储)
ALTER TABLE metrics SET (timescaledb.compress);
SELECT add_compression_policy('metrics', INTERVAL '7 days');

缓存(替代 Redis)

功能:UNLOGGED 表 + JSONB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-- UNLOGGED = 无 WAL 开销,更快的写入
CREATE UNLOGGED TABLE cache (
  key TEXT PRIMARY KEY,
  value JSONB,
  expires_at TIMESTAMPTZ
);

-- 设置过期时间
INSERT INTO cache (key, value, expires_at)
VALUES ('user:123', '{"name": "Alice"}', NOW() + INTERVAL '1 hour')
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;

-- 获取
SELECT value FROM cache 
WHERE key = 'user:123' AND expires_at > NOW();

-- 清理(使用 pg_cron 调度)
DELETE FROM cache WHERE expires_at < NOW();

消息队列(替代 Kafka)

扩展:pgmq

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CREATE EXTENSION pgmq;
SELECT pgmq.create('my_queue');

-- 发送
SELECT pgmq.send('my_queue', '{"event": "signup", "user_id": 123}');

-- 接收(带可见性超时)
SELECT * FROM pgmq.read('my_queue', 30, 5);

-- 处理后删除
SELECT pgmq.delete('my_queue', msg_id);

或使用原生 SKIP LOCKED 模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
CREATE TABLE jobs (
  id SERIAL PRIMARY KEY,
  payload JSONB,
  status TEXT DEFAULT 'pending'
);

-- Worker 原子性地声明任务
UPDATE jobs SET status = 'processing'
WHERE id = (
  SELECT id FROM jobs WHERE status = 'pending'
  FOR UPDATE SKIP LOCKED LIMIT 1
) RETURNING *;

文档存储(替代 MongoDB)

功能:原生 JSONB

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  data JSONB
);

-- 插入嵌套文档
INSERT INTO users (data) VALUES ('{
  "name": "Alice",
  "profile": {
    "bio": "Developer", 
    "links": ["github.com/alice"]
  }
}');

-- 查询嵌套字段
SELECT 
  data->>'name', 
  data->'profile'->>'bio'
FROM users
WHERE data->'profile'->>'bio' LIKE '%Developer%';

-- 索引 JSON 字段
CREATE INDEX idx_users_email ON users ((data->>'email'));

地理空间(替代专用 GIS)

扩展:PostGIS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE EXTENSION postgis;

CREATE TABLE stores (
  id SERIAL PRIMARY KEY,
  name TEXT,
  location GEOGRAPHY(POINT, 4326)
);

-- 查找 5 公里内的商店
SELECT 
  name, 
  ST_Distance(
    location, 
    ST_MakePoint(-122.4, 37.78)::geography
  ) as meters
FROM stores
WHERE ST_DWithin(
  location, 
  ST_MakePoint(-122.4, 37.78)::geography, 
  5000
);

定时任务(替代 Cron)

扩展:pg_cron

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CREATE EXTENSION pg_cron;

-- 每小时运行
SELECT cron.schedule('cleanup', '0 * * * *', 
  $$DELETE FROM cache WHERE expires_at < NOW()$$
);

-- 每晚汇总
SELECT cron.schedule('rollup', '0 2 * * *',
  $$REFRESH MATERIALIZED VIEW CONCURRENTLY daily_stats$$
);

混合搜索(BM25 + 向量)

对于 AI 应用,你通常需要关键词搜索和语义搜索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
-- 倒数排名融合:结合关键词 + 语义搜索
WITH bm25 AS (
  SELECT id, ROW_NUMBER() OVER (ORDER BY content <@> $1) as rank
  FROM documents LIMIT 20
),
vectors AS (
  SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2) as rank  
  FROM documents LIMIT 20
)
SELECT 
  d.*, 
  1.0/(60 + COALESCE(b.rank, 1000)) + 
  1.0/(60 + COALESCE(v.rank, 1000)) as score
FROM documents d
LEFT JOIN bm25 b ON d.id = b.id
LEFT JOIN vectors v ON d.id = v.id
WHERE b.id IS NOT NULL OR v.id IS NOT NULL
ORDER BY score DESC 
LIMIT 10;

试试用 Elasticsearch + Pinecone 做这个。你需要两次 API 调用、结果合并、故障处理和双倍延迟。

在 Postgres 中:一个查询,一个事务,一个结果。

模糊搜索(拼写容错)

扩展:pg_trgm(内置于 Postgres)

1
2
3
4
5
6
7
8
9
CREATE EXTENSION pg_trgm;

CREATE INDEX idx_name_trgm ON products 
USING GIN (name gin_trgm_ops);

-- 即使有拼写错误也能找到 "PostgreSQL"
SELECT name FROM products
WHERE name % 'posgresql'
ORDER BY similarity(name, 'posgresql') DESC;

图遍历(替代图数据库)

功能:递归 CTE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- 查找经理下的所有下属(组织架构)
WITH RECURSIVE org_tree AS (
  SELECT id, name, manager_id, 1 as depth
  FROM employees WHERE id = 42
  
  UNION ALL
  
  SELECT e.id, e.name, e.manager_id, t.depth + 1
  FROM employees e
  JOIN org_tree t ON e.manager_id = t.id
  WHERE t.depth < 10
)
SELECT * FROM org_tree;

底线

记住家的类比?你不会为了做晚饭就建一个独立的餐厅。你不会在城市另一端建商业车库只为停车。你使用家里的房间。

这就是我们在这里展示给你的。搜索、向量、时序、文档、队列、缓存——它们都是 Postgres 家中的房间。与专用数据库相同的算法。经过多年的实战测试。被 Netflix、Uber、Discord 和其他 48,000 家公司使用。

那 99% 呢?

对于 99% 的公司,Postgres 处理你需要的一切。那 1% 呢?那是当你在数百个节点上处理 PB 级日志时,或者需要 Kibana 的特定仪表板,或者有真正超出 Postgres 能力的异常需求时。

但事实是:当你进入那 1% 时,你会知道的。你不需要供应商的营销团队告诉你。你会自己进行基准测试并遇到真正的瓶颈。

在那之前,不要因为有人告诉你「为正确的工作使用正确的工具」而将数据分散到七个建筑中。这个建议卖数据库,但不为你服务。

从 Postgres 开始。坚持使用 Postgres。只有在你真正需要时才增加复杂性。

2026 年了,直接用 PostgreSQL 吧

运维最佳实践

性能优化

连接池配置

1
2
3
4
5
6
7
8
9
-- 使用 pgBouncer 进行连接池
# pgbouncer.ini
[databases]
mydb = host=localhost port=5432 dbname=mydb

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25

查询优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- 分析查询性能
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;

-- 创建适当的索引
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

-- 使用部分索引
CREATE INDEX idx_active_users ON users(email) 
WHERE status = 'active';

-- 表达式索引
CREATE INDEX idx_lower_email ON users(LOWER(email));

监控关键指标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- 慢查询监控
SELECT 
  query,
  calls,
  total_exec_time,
  mean_exec_time,
  max_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

-- 表膨胀检查
SELECT 
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;

-- 连接数监控
SELECT 
  datname,
  count(*) as connections,
  max_conn - count(*) as available
FROM pg_stat_activity, 
  (SELECT setting::int as max_conn FROM pg_settings WHERE name='max_connections') mc
GROUP BY datname, max_conn;

备份与恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 逻辑备份
pg_dump -Fc mydb > backup.dump

# 恢复
pg_restore -d mydb backup.dump

# 物理备份(使用 pg_basebackup)
pg_basebackup -D /backup/dir -Ft -z -P

# 时间点恢复(PITR)配置
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/%f'

高可用架构

┌──────────────────────────────────────┐
│         Load Balancer / HAProxy      │
└────────┬─────────────────────────────┘
         │
    ┌────┴────┐
    │         │
┌───▼───┐ ┌──▼────┐
│Primary│ │Standby│
│ (RW)  │ │  (RO) │
└───┬───┘ └───┬───┘
    │         │
    │ Streaming Replication
    └─────────┘

配置流复制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
-- Primary 配置
# postgresql.conf
wal_level = replica
max_wal_senders = 3
wal_keep_size = 1GB

# pg_hba.conf
host replication replicator standby_ip/32 md5

-- Standby 配置
# standby.signal 文件
standby_mode = 'on'
primary_conninfo = 'host=primary_ip port=5432 user=replicator'

安全加固

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
-- 创建只读用户
CREATE ROLE readonly_user LOGIN PASSWORD 'strong_password';
GRANT CONNECT ON DATABASE mydb TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;

-- 行级安全(RLS)
CREATE POLICY user_data_policy ON users
  FOR SELECT
  USING (user_id = current_user_id());

ALTER TABLE users ENABLE ROW LEVEL SECURITY;

-- SSL 连接
# postgresql.conf
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'

权衡与选择

优势

统一技术栈:一种查询语言、一套工具、一个备份策略 ✅ 降低运维复杂度:无需维护多个数据库系统 ✅ 成本效益:减少许可费用和基础设施成本 ✅ 数据一致性:无需跨数据库同步 ✅ AI 友好:轻松 fork 整个环境用于测试 ✅ 成熟稳定:30+ 年历史,生产级可靠性

劣势

学习曲线:需要深入了解扩展生态系统 ❌ 单点故障:所有鸡蛋在一个篮子里(通过 HA 缓解) ❌ 可能不是「最佳」:在某些极端场景下可能略逊于专用工具

何时选择 PostgreSQL

适合使用 Postgres

  • 启动新项目或重构现有系统
  • 团队规模 < 50 人
  • 需要多种数据存储能力
  • 想要简化运维
  • 构建 AI/ML 应用

考虑专用工具

  • PB 级日志分析(Elasticsearch + Kibana 的可视化)
  • 需要 Kafka 的特定流处理生态
  • 极端规模(数十亿用户级别)
  • 已有大量投资且迁移成本高

那 1% 的情况

你会知道自己是否在那 1% 中,因为:

  • 你已经对 Postgres 进行了基准测试并遇到真正的瓶颈
  • 你有专门的 DBA 团队
  • 你在处理 PB 级数据
  • 你的工程团队 > 100 人
  • 你有预算支持多个数据库团队

不是因为

  • 供应商的销售演示很吸引人
  • 你在科技博客上读到它
  • 你觉得「听起来更专业」

迁移策略

从 Elasticsearch 迁移

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 1. 安装扩展
CREATE EXTENSION pg_textsearch;

-- 2. 创建等效模式
CREATE TABLE products (
  id SERIAL PRIMARY KEY,
  name TEXT,
  description TEXT,
  price NUMERIC
);

-- 3. 创建搜索索引
CREATE INDEX idx_products_search ON products 
USING bm25(description) WITH (text_config = 'english');

-- 4. 数据迁移
-- 使用 Elasticsearch 的 scroll API 导出数据
-- 然后批量导入到 Postgres

-- 5. 更新应用代码
-- 之前: ES query DSL
-- 现在: 简单的 SQL
SELECT * FROM products
WHERE description @@ 'laptop'
ORDER BY -(description <@> 'laptop')
LIMIT 10;

从 MongoDB 迁移

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
-- 1. 创建带 JSONB 的表
CREATE TABLE orders (
  id SERIAL PRIMARY KEY,
  data JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. 创建索引
CREATE INDEX idx_orders_user ON orders ((data->>'user_id'));
CREATE INDEX idx_orders_status ON orders ((data->>'status'));

-- 3. 数据导出和导入
-- mongoexport --db mydb --collection orders --out orders.json
-- 然后使用 Python/Go 脚本转换并导入

-- 4. 查询模式转换
-- MongoDB: db.orders.find({"user_id": "123", "status": "pending"})
-- Postgres:
SELECT * FROM orders
WHERE data->>'user_id' = '123' 
  AND data->>'status' = 'pending';

从 Redis 迁移

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 1. 创建缓存表
CREATE UNLOGGED TABLE cache (
  key TEXT PRIMARY KEY,
  value JSONB,
  expires_at TIMESTAMPTZ
);

-- 2. 创建索引
CREATE INDEX idx_cache_expires ON cache(expires_at) 
WHERE expires_at IS NOT NULL;

-- 3. 设置自动清理
SELECT cron.schedule('cache_cleanup', '*/5 * * * *',
  $$DELETE FROM cache WHERE expires_at < NOW()$$
);

-- 4. 应用代码转换
-- Redis: redis.setex('user:123', 3600, json_data)
-- Postgres:
INSERT INTO cache (key, value, expires_at)
VALUES ('user:123', $1, NOW() + INTERVAL '1 hour')
ON CONFLICT (key) DO UPDATE SET 
  value = EXCLUDED.value,
  expires_at = EXCLUDED.expires_at;

总结

想象一下,如果你的数据基础设施就像你的家:

  • 一个屋檐:PostgreSQL
  • 多个房间:搜索、向量、时序、队列、缓存
  • 一套钥匙:SQL
  • 一个地址:连接字符串
  • 一次清洁:备份和维护

这就是 PostgreSQL 在 2026 年提供的:简单性、强大性和实用性的完美平衡

行动建议

  1. 评估当前架构:列出你正在使用的所有数据库
  2. 识别低挂果实:从最容易迁移的开始(通常是缓存和队列)
  3. 小步快跑:一次迁移一个用例
  4. 测量性能:确保满足你的需求
  5. 简化运维:享受减少的复杂性

记住:为正确的工作使用正确的工具 是个好建议。但当一个工具就能胜任 99% 的工作时,或许你应该重新考虑什么是「正确」。

2026 年了,直接用 PostgreSQL 吧

参考资源

官方文档

扩展资源

学习资源


本文基于 PostgreSQL 16+ 和现代扩展生态编写,适用于希望简化技术栈的开发和运维团队。