惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 聂微东
Y
Y Combinator Blog
WordPress大学
WordPress大学
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
A
About on SuperTechFans
小众软件
小众软件
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
GbyAI
GbyAI
I
InfoQ
The GitHub Blog
The GitHub Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
C
Check Point Blog
V
V2EX
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
量子位
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog

博客园 - Lafite-1820

linux 常用的命令 mac m1 使用vpn之后浏览器连不上网处理 - Lafite-1820 Opus 4.6 真的用不起了!我换成了国产 M2.5,实测真香!! 5分钟搭建完整后端服务,这款开源的快速开发神器太牛了! 高并发IoT监控系统的Go实践:Goroutine池设计与并发安全指南 服务器设置成美国时间 Linux crond nginx 配置 mysql mac m1 报错处理 秒杀系统的架构(Golang 实现) nginx 配置 git 突然 403 Vue 项目接入Google第三方登录的详细流程 mysql 添加账号 [HY000][1366] Incorrect string value: '\xF0\x9F\x8C\x9F",...' ; - Lafite-1820 mac 外接硬盘系统软件 linux 常用命令 crontab 半小时执行一次 mac berw 安装deepseek linux mv 限时文件个数
PostgreSQL 为什么不选择 B+ 树索引?
Lafite-1820 · 2025-10-16 · via 博客园 - Lafite-1820

我们知道,MySQL 的索引设计使用了 B+Tree,而 PostgreSQL 使用了 B-Tree,
那 PostgreSQL 为什么不使用 B+Tree 做索引结构呢?今天就来聊一聊这个话题。

B+Tree 和 B-Tree

B+TreeB+Tree

主键索引的叶子节点存储数据,非叶子节点(索引节点)则存储 key 和指针。这样存储的优势是可以在索引节点通过二分查找快速找到数据所在页,时间复杂度为 O(logmN),其中 N 是总的节点数量,m 是每个节点的子节点个数。找到数据页后再去数据页中找数据就很容易了。

image

B+Tree的第二个特点是叶子节点用双向链表串联起来,这样范围查询优势很大,时间复杂度为O(logmN+K)。

B-Tree跟

B+Tree不一样的是,B-tree所有节点都可以存储数据,包括根节点,内部节点,叶子节点。

image

随机查询:因为 B-Tree在非叶子节点也能存储数据,B-Tree可能在非叶子节点提前终止查询,查询路径更短。

范围查询:B-Tree查询一个数据范围时需要中序遍历多个层级,这一点效率不如 B+Tree。

PostgreSQL 索引

索引介绍

PostgreSQL 索引对 B-Tree 进行了改造。改造后的索引结构如下图:

image

上图的索引结构中最顶层是元数据页,存储索引根节点页相关信息。内部节点位于根节点下面,只包含键值和指向子页面的指针。叶子页位于最下面一层,存储所有指向实际表数据行(TIDs)的指针。

什么是 TID?PostgreSQL 采用堆表存储,数据独立于索引存储在一个无序的结构中。数据行插入时,数据库会找到一个空闲的空间来存放它,并记录一个唯一的物理地址,称为 TID,由页号和行指针组成。

因为 B-Tree的叶子节点只保存 TIDs,不保存真实数据,因此每个数据页能保存更多的叶子节点。跟 B+Tree相比,在相同数据量下,B-Tree高度更低。

PostgreSQL 索引中无论是内部节点还是叶子节点,数据都以递增顺序存储,同一层的数据页由双向链表连接。因此通过遍历链表就可以获取一个有序的数据集,范围查询并不需要中序遍历。

PostgreSQL 索引页格式如下,(下图来自官网):

image

下表对每个属性进行解释:

Item Description
PageHeaderData 24 bytes long. Contains general information about the page, including free space pointers.
ItemIdData Array of item identifiers pointing to the actual items. Each entry is an (offset,length) pair. 4 bytes per item.
Free space The unallocated space. New item identifiers are allocated from the start of this area, new items from the end.
Items The actual items themselves.
Special space Index access method specific data. Different methods store different data. Empty in ordinary tables.

三个优化

Deduplication

在索引中,如果存在大量相同的键值(比如一个被频繁更新的状态标志),PostgreSQL 会将这些重复的键值合并存储,只保留一个键值和多个对应的 TID 列表,这大大节省了空间,提高了缓存效率。

Index Only Scan

虽然叶子节点不保存完整数据,但叶子节点中除了存储键值和 TID,也可以保存查询中需要的某几个字段值(非索引列值),类似于覆盖索引。

这样,对于只查询索引列和包含列的语句,可以不用通过 TID 去堆上查找数据,直接通过索引就获取到查询结果。

反向键索引

PostgreSQL 可以创建反向排序的索引,这对于缓解插入热点(如递增主键、时间等字段)问题非常有效。创建索引的时候需要指定反向索引,例如下面 SQL 给员工编号(emp_id)创建一个反向键索引:

CREATE INDEX idx_emp_id ON tb_emploee(emp_id REVERSE);
总结

PostgreSQL 的索引结构虽然叫 B-Tree,但其实它实现了 B+Tree的功能,并且在索引上做了一些优化,使索引效率更高。