Skip to content
Charles
Go back

PostgreSQL 也可以做很多事

Edit page

引言

PostgreSQL 的价值不只是“存数据”。它有成熟的事务模型和 SQL,同时把 JSON、全文检索、空间数据、队列等能力放在同一个数据边界里。这里的结论不是 PostgreSQL 能替代一切,而是:在引入第二个系统之前,先确认 PostgreSQL 是否已经足够。

这篇文章的思路来自 PostgreSQL for EverythingSQLite for Everything,并结合 PostgreSQL 官方文档整理成更适合实际项目落地的版本。

稳定可靠

PostgreSQL 并不新潮,这反而是优点。它有很长的生产使用历史,事务、锁、索引、备份恢复和升级路径都经过了大量项目验证。新版本持续增加 JSON、分区表、CTE 和各种索引能力,同时尽量不破坏已有应用。

“老”不等于“落后”。数据库最重要的特性之一,是多年以后数据仍然能被可靠地读出来。

易于运行、安装和扩展

本地开发可以直接安装 PostgreSQL、使用容器,测试环境也可以用真实数据库跑集成测试。线上则几乎所有主流云平台都有托管 PostgreSQL,扩容、备份和高可用不必全部自己维护。

当然,托管服务不是自动高可用。连接数、慢查询、磁盘增长、备份恢复和版本升级仍然要监控;只是这些工作不必从零搭一套数据库平台。

简化 IT 架构

PostgreSQL 的关键价值在这里:它不只有关系表,还能把很多“本来要再部署一个服务”的需求留在同一个数据边界内。

用户、订单、权限、库存这类数据通常有明确的关系和约束。表、外键、唯一约束、事务和 JOIN 让数据的一致性由数据库保证,而不是散落在业务代码里。

先把核心数据建模清楚,再为少量变化快的字段使用 jsonb,通常比一开始把所有内容都塞进文档更容易维护:

CREATE TABLE products (
  id          bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name        text NOT NULL,
  attributes  jsonb NOT NULL DEFAULT '{}',
  created_at  timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX products_attributes_idx
  ON products USING gin (attributes);

SELECT id, name
FROM products
WHERE attributes @> '{"color": "black"}';

jsonb 适合承载可选属性、第三方原始响应和逐步演化的字段;金额、状态、外键这类需要约束和频繁关联的数据,仍然应该是普通列。

PostgreSQL 可以替代 Solr 和 Elastic:全文检索

PostgreSQL 自带全文检索:tsvector 负责保存词元,tsquery 负责表达查询,GIN 索引负责加速。文章、商品描述、帮助文档等中等规模搜索场景可以先这样做:

CREATE TABLE articles (
  id    bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  title text NOT NULL,
  body  text NOT NULL
);

CREATE INDEX articles_search_idx ON articles USING gin (
  to_tsvector('simple', title || ' ' || body)
);

SELECT id, title
FROM articles
WHERE to_tsvector('simple', title || ' ' || body)
      @@ plainto_tsquery('simple', '数据库');

这不是 Elasticsearch 的完整替代品。复杂的中文分词、拼写纠错、BM25 调参、海量索引和搜索集群治理,仍然可能需要专用搜索系统。可是如果需求只是“把站内文章搜出来”,先用数据库里的索引能省掉一套同步链路。

Contentful 和 Instacart 都曾经用 PostgreSQL 解决过相似的站内搜索问题。内置全文检索是一个很好的起点;如果需要更好的相关性排名或更大规模,也可以继续使用 PostgreSQL 扩展,例如 pg_textsearch 或 ParadeDB 的 pg_search,不必立即离开 PostgreSQL。

PostgreSQL 可以替代 MongoDB:JSON 文档

上一节的 jsonb 已经可以存储和查询文档。jsonb 配合 GIN 索引,适合可选属性、第三方响应和结构变化较快的内容。数据有大量关系和约束时,仍然应该使用普通列、外键和唯一约束,而不是把整个业务对象都塞进 JSON。

PostgreSQL 可以替代 Kafka 和 RabbitMQ:小型队列

后台任务不一定需要马上上 Kafka 或 RabbitMQ。任务量不大、任务结果需要和业务数据处在同一个事务里时,可以使用行锁领取任务:

CREATE TABLE jobs (
  id          bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  payload     jsonb NOT NULL,
  status      text NOT NULL DEFAULT 'pending',
  available_at timestamptz NOT NULL DEFAULT now(),
  created_at  timestamptz NOT NULL DEFAULT now()
);

BEGIN;

WITH next_job AS (
  SELECT id
  FROM jobs
  WHERE status = 'pending' AND available_at <= now()
  ORDER BY id
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
UPDATE jobs
SET status = 'running'
WHERE id IN (SELECT id FROM next_job)
RETURNING *;

COMMIT;

SKIP LOCKED 让多个 worker 不必互相等待同一条任务。实际使用时还要加超时回收、失败次数和幂等处理。队列需要跨地域复制、极高吞吐、长时间保留日志或复杂消费模型时,再换专用系统。

PostgreSQL 可以替代 ClickHouse:时序数据

高频时间数据可以使用按时间分区的表、时间索引和批量写入。数据量继续增长时,可以使用 TimescaleDB 这样的 PostgreSQL 扩展。它适合“业务数据和时序数据需要一起查询”的场景;如果主要工作负载已经变成超大规模扫描和聚合,ClickHouse 仍然是更自然的选择。

PostgreSQL 可以替代向量数据库:pgvector

使用 pgvector 可以把 embedding 和业务记录放在一起,用向量相似度检索结合租户、权限和状态过滤。原型和中等规模的 RAG 应用通常不需要再维护一套独立向量库;向量规模和召回吞吐达到瓶颈后,再拆出去。

PostgreSQL 可以替代 Redis:有限的缓存场景

可丢失、可重建的缓存可以评估 UNLOGGED 表。它不能自动变成 Redis:过期、淘汰、内存上限和高并发访问仍然需要自己设计。只是对简单缓存来说,少一个网络服务可能更值得。

PostgreSQL 可以替代文件系统:小型二进制数据

小型二进制对象可以放在 bytea 中,并和元数据、权限、事务一起管理。大文件、视频和海量静态资源通常应该放对象存储,数据库保存地址和元数据即可。

PostgreSQL 可以替代树形数据库:ltree

目录、标签和组织结构这类层级数据,可以用递归查询或 ltree 存储路径。真正需要任意方向遍历、复杂图算法的场景,才考虑专用图数据库或 Apache AGE。

PostgreSQL 作为 GraphDB 和 Neo4J 的替代方案

如果需要的是真正的图,而不只是树,就可以使用 Apache AGE。它把图查询和普通 SQL 放在同一个数据库里:

SELECT * FROM cypher('my_graph', $$
  MATCH (person:Person)-[:WORKS_AT]->(company:Company)
  RETURN person.name, company.name
$$) AS (person agtype, company agtype);

PostgreSQL 可以替代微服务

很多“微服务”只是从数据库取几列再返回 JSON。PostgreSQL 可以直接构造 JSON 结果,减少一层没有业务逻辑的胶水代码。这不是要删除所有服务,而是不要为一条简单查询预先搭建一套服务边界。

PostgreSQL 不能替代 PlayStation 5

至于用 SQL 写俄罗斯方块之类的玩法,可以证明 SQL 很有表现力,但不应该作为生产架构依据。

结论

PostgreSQL 的优势不是“什么都能做”,而是关系事务、扩展能力和各种索引都在同一个系统里。少一个服务,就少一份部署、监控、备份和数据同步成本。

下面这些情况通常说明应该认真考虑专用系统:

  1. 搜索已经成为核心产品,需要复杂的相关性、分词和独立扩容。
  2. 事件流吞吐和保留周期远超业务事务,消费者还需要独立回放。
  3. 时序写入、压缩和聚合规模已经成为数据库的主要瓶颈。
  4. 向量检索需要专门的近似最近邻索引和独立扩展策略。
  5. 数据本来就是大文件,数据库只保存元数据和对象存储地址更合适。

一个实用的顺序是:先用 PostgreSQL 完成第一版,给查询、锁、存储量和备份设置可观测指标;出现真实瓶颈后,再把最痛的那一块拆出去。这样拆分是由数据和负载推动的,而不是由架构图推动的。

新需求出现时可以先问一句:这件事 PostgreSQL 能不能用一个表、一个索引或一个扩展解决?答案经常是可以;当答案变成“会影响核心性能或运维边界”时,再引入更专业的工具。

官方资料:全文检索JSON 类型并发控制


Edit page
Share this post:

Previous Post
海外项目快速 MVP
Next Post
Git 按提交信息查找 Commit