Kevin's blog Kevin's blog
首页
  • AI基础
  • RAG技术
  • 提示词工程
  • Wireshark抓包
  • 常见问题
  • 数据库
  • 代码技巧
  • 浏览器
  • 手册教程
  • 技术应用
  • 流程规范
  • github技巧
  • git笔记
  • vpn笔记
  • 知识概念
  • 学习笔记
  • 环境搭建
  • linux&运维
  • 微服务
  • 经验技巧
  • 实用手册
  • arthas常用
  • spring应用
  • javaAgent技术
  • 网站
友情链接
  • 分类
  • 标签
  • 归档

Kevin

你可以迷茫,但不可以虚度
首页
  • AI基础
  • RAG技术
  • 提示词工程
  • Wireshark抓包
  • 常见问题
  • 数据库
  • 代码技巧
  • 浏览器
  • 手册教程
  • 技术应用
  • 流程规范
  • github技巧
  • git笔记
  • vpn笔记
  • 知识概念
  • 学习笔记
  • 环境搭建
  • linux&运维
  • 微服务
  • 经验技巧
  • 实用手册
  • arthas常用
  • spring应用
  • javaAgent技术
  • 网站
友情链接
  • 分类
  • 标签
  • 归档
  • JVM性能调优

  • 并发编程

  • MySql

    • Mysql索引底层数据结构与算法
      • 索引的本质
      • B-Tree结构
      • B+Tree结构
        • 要点:内节点只存 key,不存 data;叶子节点存全部 key;常见实现会让叶子两两串起来做顺序访问。
        • 16KB放不下怎么办?
        • 页分裂到底“向下”还是“横向”?
        • 数据量
        • 为啥是 600² ?
        • 优点:
        • 缺点:叶子集中数据,随机点查仍是“走到叶子”一跳,但这也是它以顺序/范围查询友好换来的典型取舍。
      • hash结构
      • MyISAM存储引擎索引实现
      • InnoDB存储引擎索引实现
        • 主键索引
        • 二级索引(非主键索引)
      • 联合索引
      • 为什么不是二叉/红黑树?
    • Explain详解与索引最佳实践
    • Mysql索引优化实战1
    • 慢查询知识
    • Mysql索引优化实战2
    • Mysql事务隔离级别和锁
    • Mysql锁详解
    • MVCC与BufferPool缓存机制
    • Innodb底层原理与MysqI日志机制深入剖析
    • MySQL索引背后的数据结构及算法原理
    • sql优化工具
    • sql关键字执行顺序
  • spring

  • redis

  • zookeeper

  • rabbitMQ

  • 架构

  • 锁

  • 分库分表

  • 学习笔记
  • MySql
kevin
2022-07-15
目录

Mysql索引底层数据结构与算法

# 索引的本质

  • 索引是帮助MySQL高效获取数据的排好序的数据结构

  • 索引数据结构

    • 二叉树
    • 红黑树
    • Hash表
    • B-Tree(B树)

图片1

# B-Tree结构

  • 叶节点具有相同的深度,叶节点的指针为空
  • 所有索引元素不重复
  • 节点中的数据索引从左到右递增排序

图片2

  • 要点:多路平衡查找树,节点内有多个key与指针;查找最多访问树高 h 次磁盘页。
  • 优点:把一个“节点=一页”装入内存,典型出度 d 很大(>100),相对二叉/红黑树而言树高很低(常≤3)要是和 B+Tree 比,B-Tree 会稍高一些,磁盘 I/O 次数小,适合做外存索引。
  • 缺点:内节点既存 key 也存 data,单位页能容纳的关键字个数比 B+Tree 少,区间扫描不如带顺序指针的 B+Tree 友好。

# B+Tree结构

  • B+Tree(B-Tree变种)
    • 非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
    • 叶子节点包含所有索引字段
    • 叶子节点用指针连接,提高区间访问的性能

图片3

# 要点:内节点只存 key,不存 data;叶子节点存全部 key;常见实现会让叶子两两串起来做顺序访问。

  • 一个索引页 = 一个节点 =(默认)16KB(innodb_page_size 可配置,但实例级固定)。

  • 页里有很多“项(entries)”**:

    • 内页的项 = 分隔键(目录键) + 子页指针,注意子指针数 = 项数 + 1。

    • 叶页的项:

      • 聚簇索引:主键值 + 行记录(整行;超大列会放到溢出页,这里留指针/前缀)。
      • 二级索引:二级键 + 主键值(查整行需要“回表”到聚簇索引)。
  • # 16KB放不下怎么办?

    • 页满→页分裂:新建兄弟页、把一半记录搬过去、父页插入新分隔键;必要时一路向上分裂,根分裂树高+1。

    • 行很“胖”(TEXT/BLOB/超长VARCHAR)→大块内容放到溢出页,叶页只留短指针/前缀。

    • # 页分裂到底“向下”还是“横向”?

      • 分裂本身是横向的:当某个页满了,会在同一层新建一个兄弟页,把大约一半记录搬过去;这叫页分裂(leaf split 或 internal split)。
      • 向上的是“分裂结果的传播”:分裂后需要在父页插入一条新的分隔键与子指针;如果父页也满了,它也会横向分裂,再把“需要插入父亲”的动作继续往上传播。
      • 只有根页分裂时树高才 +1:根横向分裂后,会新建一个新的根作为它们的父亲,于是高度增加一层。
  • # 数据量

    • 假设 16KB 页,内页一条目录项(分隔键+指针)算 ~24B,可放约 600 项(内页一条目录项(分隔键K+子指针P)≈24B,16KB 页可容纳≈600 条目录项。注意严格来说子指针数=目录项数+1,用“≈600”做量级估算足够了);

      叶页每条记录平均 ~100B,可放约 140 行;

      高度=3(根/内/叶) 时:可挂叶页 ≈ 600² ≈ 36 万页 → 记录 ≈ 36 万 × 140 ≈ 5000 万行;

      高度=4 时:记录量级可到 300 亿。 这就是为什么现实里 B+Tree 高度一般 2–4 层就够用的原因。

                             [ Root ]                       <-- 1 页
                     /        |        \ 
               (600个子指针) ... (按内页中一个项24B算 共最多≈600个内页)
    
             [ Internal#1 ]  [ Internal#2 ]  ...  [ Internal#600 ]   <-- 每个内页≈600个子指针
                /  |  \                                             
          (→ 600 个叶页)  ... 
    
       [ Leaf ] [ Leaf ] ... (最多≈600个)                            <-- 每个叶页≈140行
        ├─[ pk:row ] | [ pk:row ] | ... (≈140项)  ||| → 下个叶页
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 为啥是 600² ?

    以**高度=3(根/内/叶)**为例:

    • Root 页:有≈600 个子指针 → 指向≈600 个二层内页。
    • 每个二层内页:又各自有≈600 个子指针 → 各自指向≈600 个叶页。
    • 所以叶页总数 ≈ 600 × 600 = 600² = 360,000 页。

    每个叶页能装≈140 行(假设每行≈100B),因此: 总行数 ≈ 360,000 × 140 ≈ 50,400,000(约 5,000 万)。

# 优点:

  • 内节点更“瘦”,出度更大→树更矮→更少 I/O,更适合外存。
  • 叶子链表使范围/排序扫描很高效。

# 缺点:叶子集中数据,随机点查仍是“走到叶子”一跳,但这也是它以顺序/范围查询友好换来的典型取舍。

# hash结构

  • Hash
    • 对索引的key进行一次hash计算就可以定位出数据存储的位置
    • 很多时候hash索引要比B+树更高效
    • 仅能满足 “=” ,“IN” ,不支持范围查询
    • hash冲突问题

图片4

要点:对 key 做一次 hash 即可定位;MySQL 中常见于 MEMORY 引擎或某些场景。

优点:等值匹配(=、IN)非常快,很多时候比 B+Tree 等值还快。

缺点:

  • 不支持范围查询、排序、前缀匹配等。
  • 存在哈希冲突,需要额外开销处理。

# MyISAM存储引擎索引实现

  • MyISAM索引文件和数据文件是分离的(非聚集)

图片5

要点:索引文件与数据文件分离;索引叶子结点的 data 域存**“物理地址”**。主/辅索引结构一样(主键要求唯一)。

优点:

  • 索引结构简单;不依赖主键值回表,直接根据地址取行。

缺点:

  • “非聚集”带来随机 I/O更多;与 InnoDB 相比,更新/碎片后可能更易退化。

# InnoDB存储引擎索引实现

  • InnoDB索引实现(聚集)

    • 表数据文件本身就是按B+Tree组织的一个索引结构文件
    • 聚集索引-叶子节点包含了完整的数据记录
    • 为什么建议InnoDB表必须建主键,并且推荐使用整型的自增主键?(提高效率,如果没有建主键,MySQL会自动加上一列隐式ROWID,而且自增主键在构建B+Tree树的时候效率更高,因为本身自增主键就是有序的)
    • 为什么非主键索引结构叶子节点存储的是主键值?(一致性和节省存储空间)

    # 主键索引

图片6

# 二级索引(非主键索引)

图片2

# 联合索引

图片9

联合索引遵循索引最左前缀原理


要点:

  • 数据文件本身就是一棵 B+Tree:主索引(聚簇索引)叶子=整行数据。
  • **二级(辅助)索引的叶子存“主键值”**而非物理地址,二次查主键才能取到整行(“回表”)。

优点:

  • 按主键范围/顺序访问很高效;物理组织天然有序。
  • 统一以主键为“行标识”,二级索引更一致、节省存储(相对存地址可能更稳定)。

缺点与选型提示:

  • 二级索引查整行需二次检索(回表)。
  • 主键过长会让所有二级索引变“胖”(因为叶子存主键),影响空间与缓存效率;非单调主键插入触发 B+Tree 频繁分裂,写入性能差,自增主键更合适。

# 为什么不是二叉/红黑树?

  • 做外存索引最关键是把磁盘 I/O 次数降到很小;B-/+Tree 能以极小的树高达到这个目标,而红黑树等高度更深、局部性差,在磁盘上 I/O 代价明显更高,所以数据库/文件系统普遍选择 B-/+Tree。
上次更新: 2025/10/19, 22:51:18
Executor线程池原理与源码解读
Explain详解与索引最佳实践

← Executor线程池原理与源码解读 Explain详解与索引最佳实践→

最近更新
01
AI是如何学习的
10-30
02
提示词工程实践指南
10-30
03
chatGpt提示原则
10-30
更多文章>
| Copyright © 2022-2025 Kevin | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式