Lazy loaded image
PostgreSQL丨最强数据库?PostgreSQL 是什么?它的架构是怎么样的?
Words 4421Read Time 12 min
2025-12-19
type
Post
status
Published
date
Dec 19, 2025
slug
postgresql
summary
tags
技术探索
category
icon
password
你是一个后端程序员,维护着一个电商网站。
一开始,系统只是存商品、订单、用户数据,用 MySQL 很正常。
但随着业务复杂度上来,你可能会陆续遇到这些需求:
查附近门店:需要地理位置能力
商品搜索:需要全文搜索能力
跨平台商品比价:字段不固定,需要 JSON 文档能力
AI 推荐 / 语义搜索:可能需要向量检索能力
如果每个需求都单独引入一个中间件,系统可能会变成这样:
能力是增强了,但复杂度也上来了。
你要维护多个系统,还要处理数据同步、监控、备份、故障恢复和一致性问题。
这时候 PostgreSQL 的吸引力就出来了。
它仍然是一个关系型数据库,但又可以通过 JSONB、全文搜索、PostGIS、pgvector、GIN/GiST/BRIN 等能力覆盖更多复杂场景。
所以 PostgreSQL 的核心价值是:
在很多中小规模或中等复杂度场景下,用一个数据库覆盖更多能力,减少系统复杂度。

一、PostgreSQL 是什么?

PostgreSQL,简称 PG,是一个开源关系型数据库。
它和 MySQL 一样,都可以通过 SQL 读写数据:
但 PostgreSQL 不只是“能存表”的数据库,它更像一个可扩展的数据库内核
它既有传统关系型数据库的能力:
SQL
事务
索引
约束
多表 Join
也有很多高级能力:
JSONB:存储和查询 JSON 文档
PostGIS:处理地理位置和空间计算
全文搜索:支持文本检索
GIN / GiST / BRIN:支持复杂索引场景
pgvector:支持向量检索
FDW:访问外部数据源
可以简单理解成:
当然,这不代表 PostgreSQL 可以无脑替代 Redis、Elasticsearch、MongoDB。
Redis 在高性能缓存场景依然很强;Elasticsearch 在复杂搜索和日志分析场景依然更专业;MongoDB 在文档模型生态上也有优势。
PostgreSQL 的优势是:
当你的业务还没有复杂到必须拆出多个中间件时,它可以用一个数据库解决更多问题。

二、数据在 PostgreSQL 里是怎么存的?

从我们平时使用的角度看,数据库表像 Excel。
比如商品表:
product 表
id  name  price
1   手机   3999
2   电脑   6999
3   耳机   399
但真正落到磁盘上,PostgreSQL 不是按 Excel 的样子保存数据。
PostgreSQL 的普通表底层通常是堆表文件
表中的数据会被拆成一个个固定大小的数据页,默认每个数据页是 8KB
数据页里面存放一条条 tuple。
这里可以先简单理解为:
tuple 就是 PostgreSQL 里存储的一行数据,或者一行数据的某个版本。
结构大概是这样:
如果表越来越大,底层文件也会变大。
默认情况下,单个文件超过 1GB 后,PostgreSQL 会把它拆成多个段文件。
你可以把 PostgreSQL 的表存储理解成三层:
逻辑表
堆表文件
8KB 数据页
tuple
这样做的好处是,数据库查询数据时,不需要把整张表全部读进内存,只需要读取相关的数据页。

三、索引是怎么定位数据的?

有了数据页之后,问题来了:
表里有那么多 Page,PostgreSQL 怎么知道要读哪个 Page?
假设执行这条 SQL:
如果没有索引,PostgreSQL 只能从 Page 0 一直扫描到 Page N。
表小还可以接受,表大就会很慢。
所以索引的价值就是:
快速定位目标 tuple 所在的数据页和页内位置。
在 PostgreSQL 里,普通 B-tree 索引的叶子节点通常不会直接存整行数据,而是保存:
索引字段值 -> 指向堆表 tuple 的位置
这个位置可以粗略理解为:
Block Number + Offset Number
也就是:
第几个数据页 + 这个页里的第几个 tuple
比如:
id = 10086 -> Page 12, Item 3
查询时,PostgreSQL 会先走索引,再根据索引里记录的位置回到堆表取数据。
这点和 MySQL InnoDB 不太一样。
InnoDB 的主键索引是聚簇索引,叶子节点里存的就是整行数据。
而 PostgreSQL 的普通表默认是堆表结构,索引通常是通过 tuple 的位置去堆表里找数据。
简单说:
InnoDB 主键索引:索引叶子节点直接存整行数据
PostgreSQL 普通索引:索引叶子节点存 key + tuple 位置
这也是理解 PostgreSQL 存储结构时很重要的一点。

四、为什么 PostgreSQL 有这么多索引类型?

如果只是普通的等值查询、范围查询、排序,B-tree 索引就够用了。
比如:
或者:
但 PostgreSQL 的优势在于,它不只有 B-tree。
它还提供了多种索引类型,用来支持不同的数据模型和查询场景。
索引类型
适合场景
B-tree
等值查询、范围查询、排序
Hash
等值查询
GIN
JSONB、数组、全文搜索
GiST
地理位置、范围类型、空间数据
SP-GiST
空间划分、非平衡数据结构
BRIN
超大表、时序数据、天然有序数据
比如:
JSONB 查询:常用 GIN 索引
全文搜索:常用 GIN 索引
地理位置查询:常用 PostGIS + GiST 索引
超大时序表:可以考虑 BRIN 索引
所以 PostgreSQL 的索引体系不是只有一种树结构,而是一套面向不同场景的索引框架。
这也是它能覆盖 JSON、搜索、地理位置、时序数据等场景的重要原因。

五、PostgreSQL 的多进程架构:Postmaster 和后端进程

数据库不只是把数据存在磁盘上。
它还要处理客户端连接,接收 SQL,执行查询,返回结果。
PostgreSQL 采用的是多进程模型。
当一个客户端连接进来时,PostgreSQL 通常会为这个连接创建一个独立的后端进程。
也就是说:
客户端 1 -> 后端进程 1
客户端 2 -> 后端进程 2
客户端 3 -> 后端进程 3
这些后端进程负责真正执行客户端发来的 SQL。
那谁负责监听连接、创建进程、管理这些后端进程呢?
这个角色就是 Postmaster。
可以简单理解为:
Postmaster 是 PostgreSQL 的主控进程,负责监听连接、创建后端进程、管理后台进程和监控子进程状态。
结构大概是这样:
多进程模型的好处是隔离性比较好。
一个后端进程出问题,不容易直接影响其他连接。
但代价是连接太多时,进程数量和内存开销也会变大。
所以在生产环境里,PostgreSQL 经常配合 PgBouncer 这类连接池使用,避免应用直接创建大量数据库连接。

六、共享内存和后台进程:多个进程怎么协作?

多进程模型带来隔离性,但也带来一个问题:
如果每个后端进程都自己缓存数据页,内存会被大量浪费。
比如多个客户端都查询同一个商品数据页。
如果每个后端进程都从磁盘读一份到自己的内存里,就会产生大量重复缓存。
所以 PostgreSQL 使用共享内存。
多个后端进程可以共同访问这块共享内存区域。
其中最重要的几个结构是:
Shared Buffers:缓存数据页和索引页
WAL Buffers:缓存 WAL 日志
Lock Table:保存锁相关信息
查询数据时,后端进程不会直接读磁盘,而是先看 Shared Buffers 里有没有需要的数据页。
如果有,直接从内存读取。
如果没有,再从磁盘加载到 Shared Buffers。
同时,PostgreSQL 还有一些后台进程负责刷盘、写日志、清理旧版本数据。
整体架构可以这样理解:
这张图可以作为理解 PostgreSQL 架构的主图。
简单来说:
Postmaster:负责管理进程
后端进程:负责执行 SQL
Shared Buffers:缓存数据页
WAL Buffers:缓存日志
WAL Writer:把 WAL 写到磁盘
Background Writer:后台刷部分脏页
Checkpointer:推进检查点,控制恢复时间
Autovacuum:清理 MVCC 产生的旧版本数据

七、一条 SQL 在 PostgreSQL 里是怎么执行的?

现在把前面的内容串起来。
假设客户端发送一条查询 SQL:
PostgreSQL 不会直接拿这条 SQL 去读磁盘。
它会经过几个阶段:
大致可以理解成:
Parser:检查 SQL 语法
Rewriter:处理视图、规则等重写逻辑
Planner / Optimizer:选择执行计划,比如走哪个索引
Executor:按照执行计划真正执行
Access Methods:提供表和索引访问接口
Buffer Manager:管理 Shared Buffers
Storage Manager:和磁盘文件打交道
这条链路的重点是:
执行器不会直接读写磁盘,而是通过访问方法、Buffer Manager、Storage Manager 一层层完成数据访问。
简化一下,就是:
Executor
Access Methods
Buffer Manager
Storage Manager
Disk
这也是 PostgreSQL 扩展性强的原因之一。
因为数据类型、函数、索引访问方法、表访问方法等能力,都可以通过开放接口扩展出来。

八、UPDATE 和 WAL:PostgreSQL 怎么保证数据可靠?

假设执行一条更新语句:
前面的 Parser、Planner、Executor 流程和查询类似,这里不再重复。
需要关注的是写入阶段。
PostgreSQL 更新数据时,核心链路大概是:
这里最重要的是 WAL。
WAL 全称是 Write-Ahead Logging,也就是预写日志。
它的核心原则是:
数据页真正刷盘之前,必须先把对应的日志写到磁盘。
为什么要这样?
因为内存里的数据页如果还没刷盘,数据库突然宕机,修改就可能丢失。
但只要 WAL 已经落盘,PostgreSQL 重启后就可以根据 WAL 进行恢复。
所以 PostgreSQL 写数据不是每次都立刻把数据页刷回磁盘,而是:
先写 WAL
再修改 Shared Buffers 里的数据页
提交时保证 WAL 先落盘
脏页后续由后台进程异步刷回磁盘
这样既能保证可靠性,也能减少频繁刷数据页带来的性能损耗。
还有一个 PostgreSQL 的重要特点:
UPDATE 通常不是原地覆盖旧数据,而是生成新的 tuple 版本,旧版本后续由 Vacuum 清理。
这和 PostgreSQL 的 MVCC 机制有关。
这里不展开讲 MVCC,只需要先记住:
WAL:保证崩溃恢复
MVCC:支持并发读写
Vacuum:清理旧版本 tuple

九、用一张图串起来看 PostgreSQL 架构

客户端请求由 Postmaster 分配给后端进程。
后端进程经过 Parser、Planner、Executor 执行 SQL。
数据访问优先经过 Shared Buffers。
写操作通过 WAL 保证崩溃恢复。
这就是 PostgreSQL 架构的核心链路。

十、总结

PostgreSQL 首先是一个关系型数据库。
它可以像 MySQL 一样,用 SQL 管理结构化数据,支持事务、索引、约束和复杂查询。
但它又不只是普通关系型数据库。
它通过 JSONB、PostGIS、全文搜索、GIN/GiST/BRIN、pgvector 等能力,把文档、搜索、地理位置、向量检索等能力接入到同一个数据库体系里。
从存储上看,PostgreSQL 的普通表底层是堆表文件,数据被拆成默认 8KB 的 Page,Page 里存放 tuple。
从索引上看,PostgreSQL 的索引通常保存 key 和 tuple 位置,再根据这个位置回到堆表中读取数据。
从进程模型上看,PostgreSQL 采用多进程架构。Postmaster 负责管理连接和进程,后端进程负责执行 SQL,多个进程通过 Shared Buffers、WAL Buffers 等共享内存协作。
从可靠性上看,PostgreSQL 通过 WAL 保证崩溃恢复,通过 Background Writer 和 Checkpointer 异步刷写脏页,通过 MVCC 支持并发读写。
所以 PostgreSQL 的强大,不只是因为它“功能多”,而是因为它把:
存储、索引、执行器、共享内存、后台进程、WAL 日志、扩展机制 做成了一套开放的数据库内核。
PostgreSQL = 关系型数据库基础能力 + 多种高级数据类型 + 丰富索引体系 + 多进程架构 + WAL 可靠性机制 + 强扩展插件生态。
回到首页