mysql系列——底层存储结构(1)

本文深入探讨了MySQL InnoDB引擎的页结构,包括文件头、页头、最小最大记录、用户记录、空闲空间及页目录等关键部分,并解释了它们如何共同作用于数据存储与检索。

INNODB引擎

1、表空间:表创建后,会生成两个文件,.frm和.ibd  (INNODB)

     frm:表结构定义文件

     ibd:表空间文件,存储数据和索引  

2、段(SEGMENT),多个区组成,相当于表

3、区(EXTENT),一个区由64页组成,大小也就是64x16k=1M

4、页(PAGE),也叫数据块,多个行记录组成,用户数据都存储在页中,每个页大小默认为16k,若一行数据就达到16k,那此页只有一条数据,若行数据大小16K,会将溢出数据存储到溢出页中,也就是所谓的页溢出off-page

 

 

Page页可划分下面几种结构

 

下面分别介绍一下page页中各个组成部分的含义。

  • File Header:文件头信息,比较重要的信息有FIL_PAGE_PREV记录上一个page页和FIL_PAGE_NEXT下一个page页的位置信息,通过这两个信息,可以让所有的page页面组成一个双向链表:
    在这里插入图片描述

    关于文件头File Header更为详细的内容参考如下图:
    在这里插入图片描述

  • Page Header:记录本页存储记录的状态信息,比如本页记录数量,槽数量,详细的信息参考下图:
    在这里插入图片描述

  • Infimun + Supermum Records:最小行与最大行记录,是虚拟记录,标记该page页中,存储的id最大的行和id最小的行记录。具体可以参考如下图的结构:
    在这里插入图片描述

  • User Records:用户真正的数据存储区域,这里真正存放用户的行数据,它占据了整个page页的大部分空间。以单链表的形式存储一条条行记录。如下图所示,他们在物理上不一定是有序的,可能刚开始是有序的,但是随着增删改的操作可能就无序了,但是在逻辑上是有序的:
    在这里插入图片描述

    一个page页中的多行记录,再结合多个page页,就形成如下的存储结构:页与页直接是双向链表,页内的行记录直接是单向链表。如下所示:page页中的每一个箭头可以理解为一行数据
    在这里插入图片描述

 

  • Free Space:存数据空间中尚未使用的区域,该页中剩余的空间,用于存放后续插入的数据。

  • Page Directory:页目录,页中某些记录的相对位置,用于提升查询效率。我们要在一个页中查找指定的一条记录。除了从头遍历还有更高效率的方法么?Page Directory提供了解决方案。

    InnoDB会将一个页中的所有记录划分成若干个组,每组4-8个记录。将每个组最后一个记录相对于第一个记录的地址偏移量(可以定位到真实数据记录,这里指的都是主键的值)提取出来存放在页中一个叫做Page Directory的数组中,数组中的元素就是这些地址偏移量,也称为槽(slot)。所以Page Directory就是由槽组成的。

    所以在一个页中根据主键查找记录是很快的,步骤为:

    1. 二分法确定该记录所在的槽,并找到该槽所在分组中主键值最小的那条记录。
    2. 通过next_record属性遍历单链表找到记录

    注意:二分法,适用于数组。

    链表是顺序存取,不是随机存取,用二分查找并不能提高查找效率,因为你每次还得从第一个结点出发,找到指针LOW,HIGH,MIDDLE所指的元素,所以一般不在链表内使用二分查找。

  • File Trailer:文件尾,刷盘时校验页是否完整。详细内参考下图:
    在这里插入图片描述

转发自:https://blog.youkuaiyun.com/javaanddonet/article/details/111992640

 

MySQL底层数据存储结构主要依赖于其 **存储引擎(Storage Engine)**,而最常用的存储引擎是 **InnoDB**。因此,当我们讨论 MySQL底层数据存储结构时,通常是以 InnoDB 为核心来展开。 --- ## ✅ 一、核心:InnoDB 存储引擎的架构 InnoDB 使用的是 **聚簇索引(Clustered Index)** 结构来组织表数据,并基于 **B+树(B+ Tree)** 实现主键和二级索引的存储。 ### 📌 主要组成部分: | 组件 | 说明 | |------|------| | 表空间(Tablespace) | 数据物理存储的容器 | | 段(Segment) | 管理区的逻辑单位(如叶子段、非叶子段) | | 区(Extent) | 连续的 64 个页(Page),共 1MB | | 页(Page) | 最小 I/O 单位,默认 16KB | | 行(Row) | 实际的数据记录 | --- ## ✅ 二、数据存储的基本单位:页(Page) 默认大小为 **16KB(16384 字节)**,类型包括: | 页类型 | 描述 | |--------|------| | `INDEX` 页 | B+树节点,存储索引和行数据 | | `Undo` 页 | 存储事务回滚日志 | | `System` 页 | 存储系统信息 | | `Transaction` 页 | 事务系统信息 | | `Insert Buffer` 页 | 优化非唯一索引插入性能 | > ⚠️ 我们重点关注的是 `INDEX` 页 —— 它构成了 B+ 树的核心节点。 --- ## ✅ 三、表数据组织方式:聚簇索引(Clustered Index) 在 InnoDB 中: > **表数据就是按主键排序的 B+ 树结构,称为“聚簇索引”** ### 🔹 聚簇索引的特点: - 叶子节点存储完整的行数据(即数据行本身) - 非叶子节点只存主键值 + 指针 - 表必须有一个主键(如果没有显式定义,InnoDB 会自动创建一个隐藏的 6 字节 ROW ID 作为主键) #### 示例结构(B+树): ```text [Root Node] / \ [100] [200] / \ / \ [Leaf: 1~99] [Leaf: 100~199] [Leaf: 200~299] 👉 每个叶子页包含多条实际数据行 ``` --- ## ✅ 四、行格式(Row Format) 每一页中存储多行数据,InnoDB 支持多种行格式: | 行格式 | 特点 | |--------|------| | `COMPACT` | 默认,节省空间 | | `DYNAMIC` | 支持大字段(如 TEXT/BLOB)外置存储 | | `REDUNDANT` | 旧格式,兼容老版本 | | `COMPRESSED` | 压缩存储,减少 I/O | ### 🔧 行结构组成(以 DYNAMIC 为例): ```text +------------------+-------------------+---------------------+----------------+ | 隐藏字段(DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR) | 列数据 | NULL标志位 | 记录头信息 | +--------------------------------------------+--------------------+-------------+ ``` #### 隐藏字段详解: | 字段 | 作用 | |------|------| | `DB_ROW_ID` | 如果没有主键,用它做隐式主键 | | `DB_TRX_ID` | 最近修改该行的事务 ID(MVCC 用) | | `DB_ROLL_PTR` | 指向 undo log 的指针,用于构建历史版本 | --- ## ✅ 五、索引结构:B+树 vs B树 | 对比项 | B树 | B+树(InnoDB 使用) | |--------|-----|------------------| | 数据位置 | 所有节点都可能存数据 | 只有叶子节点存数据 | | 叶子连接 | 不连续 | 双向链表连接,便于范围查询 | | 查询稳定性 | 差(可能中途命中) | 好(都到叶子层) | | 范围查询效率 | 低 | 高(顺序扫描) | ✅ InnoDB 选择 B+树的原因: - 更适合磁盘 I/O(每次读一个页) - 支持高效范围查询(如 `WHERE id BETWEEN 10 AND 100`) - 层高稳定(一般 3 层可存上亿数据) --- ## ✅ 六、二级索引(Secondary Index) 除了主键索引外,其他索引都是二级索引。 ### 🔹 二级索引特点: - 叶子节点不存储完整行数据 - 存储的是 **索引列值 + 主键值** - 查找非主键字段需“回表”(通过主键再去聚簇索引查数据) #### 示例: ```sql CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50), email VARCHAR(100), INDEX idx_email (email) ); ``` `idx_email` 索引结构: ```text [ 'a@b.com' → 1 ] [ 'c@d.com' → 2 ] [ 'e@f.com' → 3 ] ``` 👉 查询 `SELECT * FROM user WHERE email='c@d.com';` 时: 1. 在 `idx_email` 中找到 `c@d.com → 2` 2. 再去主键索引中查找 `id=2` 的完整行数据 → “回表” > 💡 解决方案:使用 **覆盖索引(Covering Index)** 或 **联合索引** 避免回表。 --- ## ✅ 七、数据文件物理存储 InnoDB 将数据存储在以下几种文件中: | 文件 | 路径/说明 | |------|----------| | `.ibd` 文件 | 每张表一个文件(开启 `innodb_file_per_table`) | | `ibdata1` | 共享表空间(未开启独立表空间时所有表共用) | | `ib_logfile*` | Redo Log 文件,用于崩溃恢复 | | `frm` 文件(MySQL 5.7 及以前) | 存储表结构(8.0 后废弃) | 配置示例(my.cnf): ```ini [mysqld] innodb_file_per_table = ON # 每张表单独 .ibd 文件 innodb_page_size = 16K # 页大小 datadir = /var/lib/mysql/ # 数据目录 ``` --- ## ✅ 八、插入缓冲(Insert Buffer)与 Change Buffer 为了提升非唯一索引的写入性能,InnoDB 使用 **Change Buffer**(前身 Insert Buffer): - 当更新的索引页不在内存中时,先将变更缓存在 Change Buffer - 后续读取或后台线程再合并到磁盘页 - 适用于非唯一普通索引,不适用于主键和唯一索引(需要立即校验) --- ## ✅ 九、总结一句话: > **MySQL(InnoDB)的底层数据存储结构是以 B+ 树为基础的聚簇索引组织形式,数据按主键有序存储在 16KB 的页中,每个页构成表空间的一部分;二级索引仅保存索引列和主键值,查询时需回表;通过行格式、事务ID、回滚指针等机制支持 MVCC 和事务隔离,整个结构专为高性能磁盘 I/O 和并发控制设计。** ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值