mysql的索引结构

举报
developer_Li 发表于 2026/08/10 11:59:29 2026/08/10
【摘要】 MySQL 索引底层结构对比图========================================================================================【 InnoDB 引擎 结构 】 —— 索引即数据(主键索引树就是数据本身)=======================================================...

MySQL 索引底层结构对比图

========================================================================================
【 InnoDB 引擎 结构 】 —— 索引即数据(主键索引树就是数据本身)
========================================================================================

    [主键 B+树 索引] (聚簇索引)
         
               [ 根节点: Page 1 ]
               /      |      \
        [ ID=1 ]   [ ID=5 ]   [ ID=10 ]
         /            |             \
 [叶子节点: Page 2] <=====> [叶子节点: Page 3]  <--- (双向链表相连,极利于范围查询)
  +--------------+          +--------------+

  | ID=1 | 张三  |          | ID=5 | 李四  |  <--- (叶子节点直接存放了【整行完整数据】)
  | ID=2 | 王五  |          | ID=6 | 赵六  |
  +--------------+          +--------------+


    [ age 字段 B+树 索引] (二级索引 / 辅助索引)
         
               [ 根节点: Page 4 ]
               /              \
        [ age=18 ]         [ age=30 ]
          /                        \
 [叶子节点: Page 5]     <========>     [叶子节点: Page 6]
  +------------------+                  +------------------+

  | age=18 | 主键ID=5|                  | age=30 | 主键ID=1|
  +--------|---------+                  +------------------+
           |
           +======= (回表查询) =======> 拿着 ID=5 重新回到【主键 B+树】中
                                        从根节点开始向下查找,最终获取到“李四”整行数据


========================================================================================
【 MyISAM 引擎 结构 】 —— 索引与数据彻底分离
========================================================================================

    [.MYI 索引文件] (无论是主键还是二级索引,结构都一样)
         
               [ 根节点: Page 1 ]
               /              \
        [ ID=1 ]           [ ID=5 ]
         /                        \
 [叶子节点: Page 2]     <========>     [叶子节点: Page 3]
  +----------------------+              +----------------------+

  | ID=1 | 磁盘物理地址A |              | ID=5 | 磁盘物理地址B |  <-- (只存键值和内存指针)
  +------|---------------+              +------|---------------+

         |                                     |
         |                                     |
=========|=====================================|========================================
         v (通过物理地址直接寻址)               v (通过物理地址直接寻址)
    [.MYD 数据文件] (纯粹的数据行列表)
  +--------------------------------------------+

  | 地址A: [ ID=1 | 张三 | age=30 ]            |
  | 地址B: [ ID=5 | 李四 | age=18 ]            |  <--- (所有的实际数据全在这里按顺序排列)
  +--------------------------------------------+

InnoDB 的非叶子节点 vs 叶子节点

========================================================================================
【 非叶子节点 内部结构 】 —— 作用:纯粹的“路标”,只用来指引方向
========================================================================================
 
  [ 根节点 / 中间节点 (Page 1) ]
  +-------------------------------------------------------------------------+

  | 索引键值 (ID=1)  | 索引键值 (ID=5)  | 索引键值 (ID=10) | ... (可以存上千个) |
  |------------------|------------------|------------------|----------------|
  | 指向Page 2的指针 | 指向Page 3的指针 | 指向Page 4的指针 | ...            |
  +-------------------------------------------------------------------------+

          |                  |
          |                  +----------------------------+
          v                                               v
========================================================================================
【 叶子节点 内部结构 】 —— 作用:数据的“终点站”,存放真正的业务数据
========================================================================================

  [ 叶子节点 (Page 2) ]                              [ 叶子节点 (Page 3) ]
  +-----------------------------------+             +-----------------------------------+

  | ID=1 | 张三 | 28| 上海市...     | <=========> | ID=5 | 李四 | 32| 北京市...     |
  | ID=2 | 王五 | 19| 深圳市...     |  (双向链表) | ID=6 | 赵六 | 24| 广州市...     |
  +-----------------------------------+             +-----------------------------------+

非叶子节点的“区间指路”原理

非叶子节点里记录的键值(如 ID=1、ID=5),其实代表的是每一个子节点(页)所能接收的“最小主键值”。

========================================================================================非叶子节点 (Page 1) 的逻辑区间 】
========================================================================================

  如果你要找的 ID 满足:
  *  [大于等于 1,且小于 5]   ====> 请去 Page 2 找(包含 ID = 1, 2, 3, 4*  [大于等于 5,且小于 10]  ====> 请去 Page 3 找(包含 ID = 5, 6, 7, 8, 9*  [大于等于 10]            ====> 请去 Page 4 找(包含 ID = 10, 11, 12...========================================================================================
【 查找轨迹模拟 】
========================================================================================

  当你执行 SELECT * FROM table WHERE id = 2 时:
  1. 数据库查看非叶子节点(Page 1)。
  2. 发现 2 落在 [15) 的区间内。
  3. 于是,它顺着 ID=1 下方的指针,走向了下一层的【叶子节点 Page 2】。
  4. 在 Page 2 内部,成功找到了 ID=2 的张三。

所以在非叶子节点上,数据库不会为 id=1、id=2、id=3 分别建一排指针。非叶子节点只需要记录这个数据页的开头(最小值)ID=1,这就足够涵盖 id=2 了。id=1 和 id=2 在非叶子节点阶段走的是同一条路,直到进入了最底层的叶子节点(Page 2)后,它们才会作为两条独立的数据行分开存储。

联合索引的非叶子节点区间

联合索引(又叫复合索引)在非叶子节点上的设计非常巧妙。它的核心逻辑是:在非叶子节点中,同时存入多个列的值,并严格按照从左到右的顺序进行排序(即“最左匹配原则”的由来)。
例如: 我们建立了一个联合索引:KEY idx_name_age (name, age)。此时,非叶子节点里的“路标”就不再是单个数字,而是一个个数据元组 (name, age)。

========================================================================================联合索引非叶子节点 (Page 1) 】 —— 严格按 name 排序;name 相同时按 age 排序
========================================================================================

  | 索引键值对: (Apple, 20) | 索引键值对: (Bob, 18)   | 索引键值对: (Bob, 25)   |
  |-------------------------|-------------------------|-------------------------|
  | 指向下一层: Page 2      | 指向下一层: Page 3      | 指向下一层: Page 4      |
  +-----------------------------------------------------------------------------+

               |                         |                         |
               |                         |                         |
               v                         v                         v
========================================================================================
【 叶子节点层(数据终点)】 —— 内部同样严格按此顺序排列,且带有主键 ID(用于回表)
========================================================================================

  [ 叶子节点: Page 2 ]          [ 叶子节点: Page 3 ]          [ 叶子节点: Page 4 ]
  +--------------------+       +--------------------+       +--------------------+

  | (Apple, 20) -> ID=5|       | (Bob, 18)  -> ID=2 |       | (Bob, 25)  -> ID=9 |
  | (Apple, 25) -> ID=1| <===> | (Bob, 23)  -> ID=7 | <===> | (Charlie,10)-> ID=4|
  +--------------------+       +--------------------+       +--------------------+

它是如何进行“区间指路”的?

非叶子节点的指路逻辑与单列索引一样,依然是“看谁比我大,看谁比我小”。如果我们要查找 WHERE name = ‘Bob’ AND age = 23,数据库的寻找轨迹如下:看第一个键值对 (Apple, 20):由于字母 B 的排序在 A 后面,说明我们要找的 Bob 比 Apple 大,继续往后看。看第二个键值对 (Bob, 18):名字对上了,都是 Bob。开始比对第二个字段:我们要找的年龄是 23,而路标是 18。因为 23 > 18,说明目标在 (Bob, 18) 后面。看第三个键值对 (Bob, 25):名字一样是 Bob。比对年龄:我们要找的 23 比路标 25 小。得出结论:目标落在了 [(Bob, 18), (Bob, 25)) 这个区间内!精准定位:顺着 (Bob, 18) 下方的指针,直接切入 Page 3 叶子节点,瞬间找到了 (Bob, 23) -> ID=7。

为什么会有“最左匹配原则”?

场景 A:WHERE name = ‘Bob’(正常走索引)非叶子节点是按照 name 排序的(Apple -> Bob -> Charlie)。数据库可以直接利用二分法在非叶子节点定位到 Bob 的区间,能走索引。场景 B:WHERE age = 23(索引失效,全表扫描)如果跳过 name 只查 age,你看一眼非叶子节点里的 age:20, 18, 25。它们是无序的!因为 age 只有在 name 相同的前提下才有序。既然非叶子节点上的 age 全是乱序的,数据库就根本无法在非叶子节点做区间判断,无法指路,因此索引直接失效。场景 C:WHERE name = ‘Bob’ AND age > 18(部分走索引)name = ‘Bob’ 可以利用非叶子节点定位到 Page 3。在 Page 3 叶子节点内部,由于 age 有序,可以快速找到大于 18 的所有数据。但是,如果条件变成 WHERE name > ‘Apple’ AND age = 23,因为 name 变成了一个范围(Bob 和 Charlie),在多个不同的 name 之间,age 是无法一起排序的,所以 age 的索引在此时就会失效。

拓展

MySQL 索引的物理尺寸限制(基于 InnoDB)

在 InnoDB 中,所有的数据和索引都是以页(Page)为单位存放在磁盘上的,默认一页的大小是 16KB。
为什么 B+树只有 3-4 层就能存千万数据?假设一个主键为 BIGINT(8 字节),指针为 6 字节,一个非叶子节点的键值对大约占 14 字节。一个 16KB 的非叶子节点页面,大约可以容纳 16384 ÷ 14 ≈ 1170 个指针。如果树高为 3 层:第一层(根节点):1 个节点,指向 1170 个第二层节点。第二层:1170 个节点,指向 1170 × 1170 ≈ 1,368,900 个叶子节点。第三层(叶子节点):假设每行数据 1KB,一个叶子节点页存 16 行。总容量:1,368,900 × 16 ≈ 21,900,000(约 2190 万行数据)。也就是说,在 2000 万数据量下,InnoDB 只需要 3 次磁盘 I/O 就能定位到任意一行数据。

在 MySQL(尤其是 InnoDB 引擎)中,你每创建普通索引、唯一索引或联合索引,底层的存储引擎都会在磁盘上多建一棵全新的 B+ 树。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。