“PostgreSQL for Everything”每隔一段时间就会重新引发讨论。原因并不难理解:PostgreSQL 的核心能力很完整,扩展生态也越来越丰富,JSONB、全文搜索、逻辑复制、LISTEN/NOTIFY、分区表和丰富的索引类型,让它能够覆盖许多原本需要专用系统的场景。
但“能做”不等于“应该替代”。PostgreSQL 更像一把能力很宽的多用途工具,而 Kafka、Elasticsearch、Redis、MongoDB 和 ClickHouse 仍然在各自的性能模型、扩展方式和运维边界上占据优势。真正值得讨论的问题不是“PG 能不能替代它们”,而是:在什么规模和约束下,单一 PostgreSQL 能让系统更简单?什么时候专用系统的成本是合理的?
PG 为什么会覆盖这么多场景
PostgreSQL 的优势不只是 SQL。它把事务、约束、索引、JSON 文档、全文检索、窗口函数、物化视图、分区和复制放在了同一个数据平台里。开发团队可以用一套权限模型、一套备份策略和一套监控体系,处理关系数据、半结构化数据和一部分搜索需求。
几个能力尤其容易造成“一个数据库替代多个系统”的效果:
JSONB支持文档式数据,同时保留事务、外键和 SQL 查询能力。GIN、GiST、pg_trgm和内置全文搜索能够覆盖许多站内搜索场景。LISTEN/NOTIFY适合轻量级进程通知,不需要额外部署消息组件。- 表分区、逻辑复制和并发控制可以支撑不少事件记录、任务队列和审计场景。
- 扩展机制允许团队按需增加地理数据、向量搜索或其他领域能力。
这些能力降低了系统数量,也降低了跨系统同步、数据一致性和权限管理的复杂度。不过,所有能力都共享同一套 PostgreSQL 资源。写入高峰、复杂搜索和大规模分析如果同时发生,可能互相争抢 CPU、内存、磁盘 I/O 和连接数。
四类替代关系要分开看
Kafka:可以承接事件,但不是天然的日志平台
PostgreSQL 表可以存储业务事件,事务提交时还能保证“业务数据和事件记录一起成功”。通过 Outbox 模式,应用先把事件写入表,再由消费者批量读取并发布到下游,这在中小规模系统中很实用。
但 Kafka 的核心优势是高吞吐追加日志、分区扩展、消费者组和较长时间的事件保留。PostgreSQL 的表锁竞争、索引维护、WAL 压力和 vacuum 成本,会让它在大量独立消费者、持续高写入和长时间回放场景下变得不合适。
结论是:PG 可以替代简单消息队列或事件中转层,但不应默认替代高吞吐事件流平台。
Elasticsearch:适合产品级搜索,不等于搜索集群
PostgreSQL 内置全文搜索和 pg_trgm 对标题、正文、模糊匹配、过滤和排序已经够用。数据量可控、搜索字段稳定、要求事务内可见的业务系统,直接在 PG 中搜索通常更简单。
Elasticsearch 更适合复杂相关性排序、分词分析、多字段检索、聚合、搜索集群横向扩展,以及对搜索索引和事务库进行明确隔离的架构。如果搜索体验是产品核心,或者需要持续承受大量搜索请求,PG 的搜索能力不应被包装成 Elasticsearch 的完全替代品。
Redis:缓存和极低延迟仍然是不同问题
PG 可以通过物化视图、合适的索引和连接池减少很多缓存需求,也可以用表实现可靠任务队列。但 Redis 的内存数据结构、单线程事件循环、极低延迟和过期策略,是 PostgreSQL 不会自然提供的组合。
如果缓存命中率、毫秒以下延迟、排行榜、计数器、分布式锁或短生命周期状态是关键指标,Redis 仍然更合适。把所有临时数据写进 PG,可能让主库承受大量本可丢弃的读写流量。
MongoDB:JSONB 很强,但文档模型并未消失
JSONB 允许团队渐进式存储半结构化数据,并且可以通过表达式索引和 GIN 索引查询内部字段。对“多数字段稳定、少数字段灵活”的业务,PG 往往比单独引入文档数据库更省事。
如果数据天然是聚合文档,写入结构变化频繁,查询主要围绕整篇文档展开,或者需要围绕文档模型进行独立扩展,MongoDB 仍有其合理位置。JSONB 不是免费午餐:过度依赖无约束文档会削弱数据质量,更新大文档也可能放大写入和膨胀成本。
一个可实践的 PostgreSQL 方案
下面的示例假设使用 PostgreSQL 14 或更高版本。它把订单、JSONB 扩展字段、全文搜索和 Outbox 事件放在同一个数据库中。代码可以直接在测试数据库执行;生产环境还需要补充权限、分区、清理策略和监控。
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
customer_id BIGINT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('pending', 'paid', 'cancelled')),
details JSONB NOT NULL DEFAULT '{}'::jsonb,
description TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX orders_details_gin_idx
ON orders USING GIN (details);
CREATE INDEX orders_description_search_idx
ON orders USING GIN (to_tsvector('simple', description));
CREATE INDEX orders_sku_trgm_idx
ON orders USING GIN ((details->>'sku') gin_trgm_ops);
CREATE TABLE outbox_events (
id BIGSERIAL PRIMARY KEY,
aggregate_id BIGINT NOT NULL,
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
published_at TIMESTAMPTZ
);
BEGIN;
INSERT INTO orders (customer_id, status, details, description)
VALUES (
42,
'paid',
'{"sku": "KB-100", "channel": "web"}',
'PostgreSQL operations handbook'
)
RETURNING id;
INSERT INTO outbox_events (aggregate_id, event_type, payload)
VALUES (
currval('orders_id_seq'),
'order.paid',
'{"customer_id": 42, "sku": "KB-100"}'
);
COMMIT;
-- 消费者批量领取未发布事件,避免多个消费者重复处理
WITH batch AS (
SELECT id
FROM outbox_events
WHERE published_at IS NULL
ORDER BY id
FOR UPDATE SKIP LOCKED
LIMIT 100
)
UPDATE outbox_events AS e
SET published_at = now()
FROM batch
WHERE e.id = batch.id
RETURNING e.id, e.event_type, e.payload;
这个方案适合把数据库事务和领域事件绑定在一起。消费者真正发布到外部系统时,应采用“读取、发布、确认”或幂等处理策略,而不是在数据库事务里假设网络调用必然成功。上面的 published_at 标记也只是最小示例,生产实现通常会增加重试次数、错误信息、租约或独立的发布状态。
搜索则可以这样写:
SELECT id, customer_id, status, description
FROM orders
WHERE to_tsvector('simple', description)
@@ plainto_tsquery('simple', 'PostgreSQL')
AND details @> '{"channel": "web"}'::jsonb
ORDER BY created_at DESC
LIMIT 20;
这里的关键不是把所有专用系统都删除,而是把低复杂度、强一致和中等规模的需求收拢到一个数据边界内。
判断是否应该引入专用系统
可以用下面几组问题做架构决策:
- 数据是否必须和核心业务事务原子提交?如果是,优先考虑留在 PG,或采用 Outbox 与专用系统解耦。
- 工作负载是否有明显的单一特征?高吞吐追加日志、极低延迟缓存、复杂搜索和大规模列式分析,通常更适合专用系统。
- 峰值流量是否会影响主库?不要只看平均 QPS,要评估峰值写入、索引维护、备份、vacuum 和复制延迟。
- 团队是否有能力维护第二套系统?专用系统带来更好的局部性能,也带来容量规划、数据同步、故障恢复和权限治理成本。
- 失败时能否接受数据延迟或重复?搜索索引和缓存通常可以异步重建,订单、支付和审计数据则不能采用同样的容错假设。
结论:减少系统数量,而不是追求系统数量为一
PostgreSQL 的合理定位是“功能覆盖面很宽的主数据平台”。它可以替代许多低规模、低复杂度的独立组件,也可以成为事件、搜索和半结构化数据的起点。但它并不会自动拥有 Kafka 的事件流吞吐、Redis 的延迟、Elasticsearch 的搜索集群能力或 ClickHouse 的列式分析效率。
更稳妥的落地方式是:先用 PG 解决有明确事务边界的核心数据,测量真实的读写、延迟、数据量和故障恢复需求;当某个工作负载形成清晰瓶颈,再为它引入专用系统。这样得到的是可验证的演进,而不是围绕“万能数据库”口号做架构迁移。