MySQL 8 索引与 B+ 树-初浅理解

一、为什么要理解索引结构?

我们平常写下的 SQL:

sql 复制代码
SELECT * FROM user WHERE id = 100;

虽然只是一行查询语句,但 MySQL 背后是如何"知道"该去哪一页找数据的?

为什么即使有上千万行,也能瞬间查到?

核心答案只有三个字:B+ 树


二、索引的本质是什么?

在 InnoDB 引擎中:

  • 索引(Index)不是"附加文件",而是数据的有序组织结构
  • MySQL 通过 B+ 树(Balanced Plus Tree) 实现高效查找;
  • 所有表数据都存储在主键 B+ 树的叶子节点中。

一张表,其实就是一棵以主键为索引的 B+ 树。


三、建表示例

sql 复制代码
CREATE TABLE A (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50),
  age INT
) ENGINE=InnoDB;
  • id 是主键;
  • MySQL 自动为它建立聚簇索引(Clustered Index)。

四、插入数据:B+ 树的起点

sql 复制代码
INSERT INTO A (name, age) VALUES ('Tom', 18);
INSERT INTO A (name, age) VALUES ('Jerry', 22);

表中数据:

id name age
1 Tom 18
2 Jerry 22

初始结构(单页)

复制代码
┌────────────────────────────────┐
│  Root/Leaf Page(同一个页)     │
│--------------------------------│
│ id=1 | name=Tom | age=18        │
│ id=2 | name=Jerry | age=22      │
└────────────────────────────────┘

只有一个页(Page),所有数据都在这个叶子页中。


五、页分裂:B+ 树如何"长大"

InnoDB 的每个数据页大小为 16KB

一行约 60 字节,一个页能存 约 270 行

当插入第 271 行时,页满了,触发 页分裂(Page Split)


分裂前

复制代码
Page#1
┌─────────────────────────────┐
│ id=1 ~ id=270               │
│ (已满)                      │
└─────────────────────────────┘

分裂后

复制代码
           Root Page
           ┌───────────────┐
           │ key = 135     │
           └───────────────┘
              /         \
             /           \
┌────────────────┐   ┌────────────────┐
│ Page#1: id 1~135 │   │ Page#2: id 136~270 │
└────────────────┘   └────────────────┘

此时树高 = 2 层(Root + Leaf)。


六、数据继续增长:形成三层结构

当页越来越多时,Root 页也可能装不下所有指针,

此时会再次分裂,形成 3 层结构。

复制代码
                     Root
                  (Level 3)
                     │
     ┌───────────────┴───────────────┐
     ▼                               ▼
 Internal Page#10               Internal Page#20
     │                               │
     ▼                               ▼
┌────────────┐                 ┌────────────┐
│Leaf Page#1 │──▶ ... ──▶│Leaf Page#N │
└────────────┘                 └────────────┘
  • Root:保存中间节点指针
  • Internal Node:保存页号范围
  • Leaf Node:保存完整行数据
  • 所有 Leaf 之间用双向链表连接,便于范围查询。

七、普通索引(二级索引)结构

再创建一个索引:

sql 复制代码
CREATE INDEX idx_name ON A(name);

生成一棵独立的 B+ 树(存储 name 和对应主键 id):

复制代码
         idx_name(二级索引)
                 Root
                  │
       ┌──────────┴──────────┐
       ▼                     ▼
   name=Jerry,id=2      name=Tom,id=1

二级索引的叶子节点不存整行数据,只存"索引列 + 主键 id"。

当用 name 查找时,需要回表


回表示意

sql 复制代码
SELECT * FROM A WHERE name = 'Tom';

执行过程:

  1. 在 idx_name 树查 name='Tom' → 得到 id=1

  2. 去主键树查 id=1 → 获取完整行

    idx_name树: name='Tom' → id=1

    主键树: id=1 → (Tom, 18)


八、B+ 树查找过程

以三层结构为例:

复制代码
           [Root]
             │
         ┌───┴───┐
         ▼       ▼
    [Internal] [Internal]
         │
         ▼
      [Leaf Pages]

查找 id=900000 时:

  1. 从 Root 判断区间;
  2. 进入对应的 Internal Page;
  3. 定位到 Leaf Page;
  4. 顺序扫描到匹配记录。

全程仅需 3 次磁盘 I/O(树高 3)。


九、页分裂与性能影响

插入模式 分裂频率 碎片 性能
自增主键 极少 ✅ 最优
随机 UUID 频繁 ❌ 慢
批量插入 可控 👍 稳定

建议使用自增主键,保证插入顺序、减少分裂与碎片。


十、百万级数据的树高度

层级 每节点页数 可容纳行数
叶子页 270 行 270
第二层 1000 页 27 万
第三层 1000 页 2.7 亿

百万级数据时,树高通常只有 3 层。

查找只需 3 次磁盘 I/O。


十一、整体结构回顾图

复制代码
                   [Root]
                     │
    ┌────────────────┴────────────────┐
    ▼                                 ▼
[Internal#1]                    [Internal#2]
    │                                 │
    ▼                                 ▼
[Leaf#1] ⇄ [Leaf#2] ⇄ ... ⇄ [Leaf#N]
  • Root:索引入口
  • Internal:保存页指针
  • Leaf:存放整行数据
  • 叶子之间用双向链表连接(便于范围扫描)

十二、总结

概念 说明
聚簇索引 表数据即索引叶子
二级索引 需通过主键回表
页分裂 数据页满后自动拆页
B+ 树高度 通常 3~4 层即可支持亿级数据
自增主键 能保持索引有序、减少分裂

十三、一句话总结

在 MySQL 8 中,索引不是"附加文件",

而是数据本身的有序存储结构。

理解 B+ 树结构,就理解了 MySQL 的性能之魂。

相关推荐
南墙上的石头12 小时前
麒麟 V10 重装人大金仓 V8R6 踩坑实录(含 MySQL 兼容模式)
数据库·mysql
执子手 吹散苍茫茫烟波14 小时前
RC 隔离级别下 MySQL InnoDB 死锁典型案例
数据库·mysql
峥无18 小时前
深入理解MySQL事务与MVCC机制
数据库·mysql
要开心吖ZSH21 小时前
MVCC 进阶:快照读 vs 当前读、幻读与 Next-Key Lock
java·数据库·sql·mysql·mvcc
万亿少女的梦16821 小时前
基于Spring Boot的天空影院电影网站系统设计与实现
java·spring boot·mysql·vue·系统设计
万亿少女的梦16821 小时前
基于Spring Boot的社区管理系统设计与实现
java·spring boot·mysql·vue·系统设计
翔云1234561 天前
简单概括主库上 Executed_Gtid_Set 是什么时候更新的
数据库·mysql
要开心吖ZSH1 天前
Java事务与MySQL事务的关系及MVCC通俗解析
java·开发语言·mysql·mvcc
爱喝热水的呀哈喽1 天前
hypermesh两个网格参数解析
服务器·数据库·mysql
万亿少女的梦1681 天前
基于Spring Boot的楚雄旅游景区门票售卖系统设计与实现
java·spring boot·mysql·vue·系统设计