DB-Engines 六月份的一篇文章显示,PostgreSQL(以下简称 PG)的热度仍在持续上升,上半年的涨分幅度位列第一。

而在最新的数据库排行中,PG 也已经位列第四。

PG 这些年的热度增长,与开源、社区生态以及数据库市场本身的变化都有关系。
不过真正用过一段时间之后,会发现 PG 本身也确实有很多很有辨识度的设计。
除了普通的表、事务、索引和 JOIN,它还原生支持 JSONB、数组、Range、全文检索以及多种索引类型,再配合 PostGIS、pgvector、zhparser 等扩展,可以处理的数据类型比很多传统关系型数据库更加丰富。
我个人认为,PG 最有价值的一点在于:当业务需要一种新能力时,往往可以直接通过扩展在现有数据库体系内实现,而不必再额外引入一套新系统。
这篇文章就挑几个我觉得比较有代表性的地方聊一聊。
一、JSONB:关系型数据库里直接处理半结构化数据
PG 对 JSON 的支持已经深入到了数据库类型和查询体系中。
其中最常用的就是 jsonb:
CREATE TABLE devices (
id bigint PRIMARY KEY,
name text,
properties jsonb
);
例如不同设备可以拥有不同属性:
{
"model": "A100",
"temperature": true,
"voltage": 220
}
查询时可以直接访问 JSON 内部字段:
SELECT *
FROM devices
WHERE properties->>'model' = 'A100';
也可以判断 JSON 是否包含某段结构:
SELECT *
FROM devices
WHERE properties @> '{"temperature": true}';
JSONB 还可以配合 GIN 索引,对内部字段和包含关系进行加速。
这种设计在实际项目中很方便。
稳定、经常参与查询和关联的字段正常建列;设备参数、扩展配置、动态属性这一类结构变化较多的数据,则可以放进 JSONB。
这样既能保留关系型结构,又不需要为了几个不稳定字段频繁修改表结构。
二、数组本身就是一种字段类型
PG 可以直接把数组作为字段类型。
例如:
CREATE TABLE articles (
id bigint PRIMARY KEY,
title text,
tags text[]
);
一篇文章可以直接保存:
{"PostgreSQL","Database","Backend"}
查询是否包含某个标签:
SELECT *
FROM articles
WHERE tags @> ARRAY['PostgreSQL'];
这种写法也很有 PG 的风格。
像标签、权限集合、简单分类、少量状态集合,有时候关系并没有复杂到值得额外建立一张关联表。
这种情况下,数组可以让表结构更加直接。
当然,如果数组中的元素本身需要独立管理,或者涉及复杂关联、统计和约束,正常拆表通常还是更加合适。
三、Range:直接把“范围”当成数据
PG 原生提供 Range 类型,例如:
daterange
tsrange
tstzrange
int4range
int8range
于是预约时间可以直接定义成:
CREATE TABLE reservations (
id bigint,
period tstzrange
);
查询某段时间是否与已有预约重叠:
SELECT *
FROM reservations
WHERE period &&
'[2026-09-09 10:00, 2026-09-09 12:00)'::tstzrange;
这里的 && 表示两个范围存在重叠。
常见的设计通常会使用:
start_time
end_time
然后在查询中组合大于、小于等条件。
Range 则把“区间”本身变成了一种数据类型,同时提供包含、重叠、相邻等专门的运算。
在预约系统、任务执行窗口、会员有效期、价格区间等场景里,这种表达方式会自然很多。
四、Exclusion Constraint:把“不能冲突”写成数据库约束
数据库中最常见的唯一性约束是:
UNIQUE
它很适合解决“两个值不能相同”的问题。
但实际业务里还有很多规则并不是简单的相等关系。
例如:
同一个会议室的预约时间不能重叠。
PG 可以通过 EXCLUDE 直接表达:
EXCLUDE USING gist (
room_id WITH =,
period WITH &&
);
它表示:
同一个 room_id 下,两个 period 不能发生重叠。
这样一来,“预约时间不能冲突”就成为了数据库自身维护的规则。
即使以后存在多个服务、脚本或者后台任务同时写入数据,这条约束仍然有效。
相比只依赖应用层先查询、再判断,这种方式对并发场景也更加可靠。
五、全文检索和预分词
PG 自带 Full Text Search,并提供:
tsvector
tsquery
这样的专门类型。
例如:
SELECT to_tsvector(
'english',
'PostgreSQL provides powerful full text search'
);
数据库会把文本转换成适合搜索的词元表示。
实际项目中通常还会把处理后的 tsvector 保存下来,并建立 GIN 索引。
这样查询时可以直接使用已经生成好的搜索向量,避免每次搜索都重新处理整篇文本。
相比:
LIKE '%keyword%'
全文检索可以进一步处理词元、搜索条件组合以及相关度排序。
对于博客、文档、知识库以及包含大量文本字段的系统,这已经是一套相当完整的基础搜索能力。
六、zhparser:把中文分词接入 PostgreSQL
PG 自带的全文检索对英文比较自然,因为英文单词之间本身就有明显的空格边界。
中文处理起来会更复杂一些。
例如:
实验室数据质量管理平台
搜索系统首先要确定如何拆分成:
实验室
数据
质量
管理
平台
zhparser 就是 PG 生态中比较常见的中文分词扩展。
它可以把中文分词能力接入 PG 原本的全文检索体系,因此后续仍然能够继续使用:
tsvector
tsquery
GIN
这一整套机制。
对于内部管理系统、知识库、文档平台这类搜索规模没有特别夸张的项目,PG 配合 zhparser 已经可以承担不少中文搜索需求。
这样也能少维护一套额外的搜索基础设施。
七、PostGIS:地理位置不再只是两个浮点数
最简单的经纬度保存方式一般是:
latitude
longitude
但只要开始真正做地理查询,事情很快就会复杂起来。
例如:
- 距离某个位置 5 公里以内有哪些设备;
- 一个点是否位于某个区域;
- 两个区域是否相交;
- 一条路线经过了哪些区域。
PostGIS 是 PG 最具代表性的扩展之一。
它加入了:
geometry
geography
等空间数据类型。
例如:
SELECT *
FROM devices
WHERE ST_DWithin(
location,
:target_location,
5000
);
数据库可以直接完成空间距离计算。
除此之外,PostGIS 还能够处理不同坐标系之间的转换,以及点、线、面之间的大量空间关系。
对于地图、物流、设备管理、物联网(IoT)、车辆轨迹和区域分析等系统,这种能力非常实用。
八、pgvector:PostgreSQL 也可以直接做向量搜索
Embedding 普及之后,很多系统开始需要保存向量数据。
例如一段文本经过模型处理后,可能得到:
[0.13, -0.27, 0.81, ...]
pgvector 为 PG 增加了真正的 vector 类型。
例如:
CREATE TABLE documents (
id bigint PRIMARY KEY,
content text,
embedding vector(1536)
);
之后就可以直接按照向量距离寻找相似内容:
SELECT *
FROM documents
ORDER BY embedding <=> :query_vector
LIMIT 10;
pgvector 还支持 HNSW、IVFFlat 等向量索引。
我觉得它比较有吸引力的一点,是向量数据可以继续和普通业务数据放在同一套查询体系中。
例如:
SELECT *
FROM documents
WHERE project_id = 100
ORDER BY embedding <=> :query_vector
LIMIT 10;
先按照 project_id 过滤业务范围,再进行向量相似度排序。
对于检索增强生成(RAG)、内部知识库和中等规模的语义搜索系统,这种组合方式可以减少不少额外架构。
如果业务量继续扩大、搜索需求进一步复杂,再考虑专门的向量数据库也完全来得及。
九、GIN、GiST、BRIN:PostgreSQL 的索引不只有 B-Tree
PG 的索引体系也很有特点。
除了最常见的 B-Tree,还经常可以看到:
- GIN:常用于 JSONB、数组、全文检索等“一条数据包含多个元素”的场景;
- GiST:常见于 Range、空间数据以及更复杂的关系查询;
- BRIN:适合大型、数据值与物理存储顺序高度相关的表,例如持续追加的日志和时序数据。
例如一张长期按照时间顺序追加的亿级日志表,如果每条记录的时间和物理存储位置本身就高度相关,那么 BRIN 会很有意思。
它不需要像普通 B-Tree 一样记录大量精细索引项,而是记录一段数据块的大致取值范围。
查询某个时间范围时,就可以快速排除大量完全无关的数据块。
所以 PG 索引体系真正有意思的地方,在于不同的数据特征可以使用完全不同的索引策略。
十、扩展机制才是这些能力背后的基础
前面提到的:
zhparser
PostGIS
pgvector
都属于 PG 扩展生态的一部分。
PG 的扩展机制很深入。
扩展可以加入:
新的数据类型
新的函数
新的运算符
新的索引能力
新的查询行为
例如 PostGIS 安装之后,空间数据就可以直接参与:
SELECT
WHERE
JOIN
INDEX
ORDER BY
这些常规数据库操作。
pgvector 也是类似。
向量会成为 PG 可以识别、计算和建立索引的数据类型。
因此 PG 的扩展生态能够不断出现新的能力,同时又保持在原有 SQL、事务、权限和索引体系之内。
我觉得这其实是 PG 很有辨识度的一点。
PostgreSQL 的优势到底体现在哪里
如果业务只有几张表,再加上一些简单CRUD,那么 PG 的很多能力确实很难体现出来。
随着系统的数据类型逐渐复杂,它的特点才会越来越明显。
例如一个系统可能同时存在:
关系数据
动态 JSON
标签数组
中文文档
地理位置
Embedding
使用 PG 时,可以根据数据自身的特点选择对应能力。
普通业务关系继续使用表、外键、事务和 JOIN;
动态属性使用 JSONB;
标签集合可以考虑数组;
时间窗口可以使用 Range;
全文搜索使用 tsvector;
中文分词接入 zhparser;
空间数据交给 PostGIS;
语义搜索则可以使用 pgvector。
这些能力又可以继续共享 PG 原有的事务、权限、备份和 SQL 查询体系。
这也是我觉得 PG 很有价值的地方:
随着业务复杂度提高,它依然可以继续容纳更多不同形式的数据,很多需求不用一开始就拆成好几套独立基础设施。
当然,这并不意味着 PG 可以取代所有专业系统。
数据规模、访问模式和业务需求到了某个阶段,ES、专业时序数据库、向量数据库或者其他系统依然有自己的优势。
PG 更吸引人的地方,在于很多项目可以先从一套相对统一的数据库体系开始,等真正出现明确瓶颈之后再拆分。

696

被折叠的 条评论
为什么被折叠?



