01 - 索引的本质:为什么能加速
数据库优化,第一件事永远是索引——它性价比最高、见效最快。这一章不背命令,而是让你从直觉上理解索引为什么能让查询快成百上千倍,以及它的代价。
一句话先记住:索引就像书的目录——不用一页页翻(全表扫描),直接翻到目录定位页码。代价是目录本身要占空间、书更新时目录也得跟着改。
1.1 没有索引,数据库怎么找数据
假设一张 100 万行的 users 表,你要查 WHERE email = 'a@x.com':
没有索引 → 全表扫描(Sequential Scan)
从第 1 行开始,一行一行比对 email
第 1 行?不是。第 2 行?不是。……第 999999 行?不是。第 1000000 行?是!
→ 最坏要看 100 万行,慢得要死
数据量小时无所谓,数据量一大,全表扫描就是灾难。
1.2 有了索引,就像查字典
索引是数据库为某个列提前排好序、建好的一个查找结构。查字典时你不会从第一页翻,而是用拼音/部首目录直接定位。
email 上建了索引后:
索引里的 email 是排好序的,数据库用「二分查找」快速定位
100 万行 → 只需要约 20 次比较就能找到(log₂ 1000000 ≈ 20)
→ 从「看 100 万行」变成「看 20 次」,快了几万倍
1.3 索引的底层结构:B+ 树(直觉版)
绝大多数数据库索引(包括 PostgreSQL 默认索引)用的是 B+ 树。你不需要会实现它,只要有这个直觉:
[50] ← 根节点
/ \
[20, 35] [70, 90] ← 中间节点(像目录的目录)
/ | \ / | \
... ... ... ... ... ... ← 叶子节点(存实际的键 + 指向数据的位置)
└──────────────────────────┘
叶子节点之间还用链表连起来 → 范围查询(如 age > 30)也很快
为什么 B+ 树快:
- 层数很少:几百万行数据,树也就 3-4 层。查一个值最多走 3-4 步就到叶子
- 有序:天然支持
=、>、<、BETWEEN、ORDER BY - 叶子相连:范围扫描时顺着链表走即可,不用回头
记住这个数字感:几百万行的表,B+ 树索引查一条数据,通常只需要 3-4 次磁盘访问。 这就是它快的根源。
1.4 聚簇索引 vs 非聚簇索引
这是一个重要概念,不同数据库实现不同:
聚簇索引(Clustered):
索引的叶子节点,直接就是「数据本身」
→ 数据行按索引顺序物理存储
→ 找到索引 = 找到数据,一步到位
非聚簇索引(Non-clustered / 二级索引):
索引的叶子节点,存的是「指向数据行位置的指针」
→ 找到索引后,还要再顺着指针去取真正的数据行(叫"回表")
PostgreSQL 的特点:PostgreSQL 没有 MySQL InnoDB 那种「主键即聚簇索引」的设计。它的表数据独立存放(叫 heap),所有索引(包括主键索引)都是「指向数据物理位置」的,性质上更接近「非聚簇」。所以在 PG 里,通过索引查到后,通常都要再去 heap 取数据行。(这也让「覆盖索引」在 PG 里格外有价值,见第 02 章。)
1.5 索引的代价(为什么不能乱建)
索引不是免费的,也不是越多越好:
① 占空间
索引本身是一份额外的数据结构,要占磁盘(大表的索引可能很大)
② 拖慢写入
每次 INSERT / UPDATE / DELETE,不光改数据,
所有相关索引也都要跟着更新(维护 B+ 树的有序性)
→ 索引越多,写越慢
③ 可能没用
给一个「很少被查询、或区分度很低」的列建索引,纯属浪费
(比如给"性别"列建索引,就两个值,索引几乎没用)
| 好处 | 代价 |
|---|---|
| 查询快(读加速) | 占额外空间 |
| 支持排序、范围查询 | 拖慢写入(增删改) |
核心权衡:索引是「用空间 + 写入变慢」换「查询变快」。读多写少的列值得建索引;写极频繁、或很少查询的列要谨慎。
1.6 什么样的列适合建索引
✅ 适合:
· 经常出现在 WHERE 条件里的列
· 经常用于 JOIN 连接的列(外键)
· 经常用于 ORDER BY / GROUP BY 的列
· 区分度高的列(值很分散,如 email、手机号、id)
❌ 不适合:
· 区分度极低的列(性别、状态只有几个值)
· 很少被查询的列
· 频繁更新、但很少用于查询条件的列
· 超大文本字段(应考虑专门的全文索引)
本章小结
| 你要记住 | 要点 |
|---|---|
| 索引本质 | 像书的目录,避免全表扫描 |
| 底层结构 | B+ 树,几百万行只需 3-4 层 |
| 聚簇 vs 非聚簇 | PG 的表数据独立存放,索引都要「回表」取数据 |
| 代价 | 占空间 + 拖慢写入,不能乱建 |
| 建索引原则 | 高频查询、高区分度的列才值得建 |
下一章预告:知道了索引原理,实战中还有一堆讲究——联合索引怎么建、什么是覆盖索引、为什么你建了索引却「没走索引」。
下一章 → 02 - 索引优化实战