当前位置: 首页 > web >正文

MySQL InnoDB记录存储结构深度解析

1. InnoDB存储引擎概述

InnoDB是MySQL默认的事务型存储引擎,广泛用于OLTP(在线事务处理)场景。它的设计目标是保证数据完整性、事务安全性和高并发性能。理解InnoDB的记录存储结构,必须先掌握其宏观架构和核心机制。

1.1 存储架构与页(Page)概念

InnoDB的存储结构可以分为以下几个层次:

  1. 表空间(Tablespace)

    • 数据物理存储单元,可以是共享表空间(ibdata1)独立表空间(.ibd文件)

    • 表空间中存储的数据被划分为页(Page),页是InnoDB最小的I/O单位。

    • 默认页大小为16KB,可通过innodb_page_size调整。

  2. 页(Page)

    • 每个页类似于硬盘上的“集装箱”,里面存储行记录、页目录、页头信息等。

    • 页类型主要有:

      • 数据页(Index Page):存储B+树叶子节点记录。

      • 非叶子节点页(Interior Page):存储B+树索引信息(键值+指针)。

      • BLOB页(Overflow Page):存储大字段的溢出数据。

      • Undo页:存储事务回滚信息。

  3. 行记录(Row)

    • 页中的基本存储单元,每行由固定长度字段 + 变长字段 + NULL标志位组成。

    • InnoDB支持不同行格式(REDUNDANT、COMPACT、DYNAMIC、COMPRESSED),每种格式在页中的存储布局不同。

⚡ 类比说明:页就像“集装箱”,行记录是集装箱里的“箱子”,字段则是箱子里的具体物品。页目录相当于箱子编号,方便快速查找。

1.2 事务支持与ACID特性

InnoDB支持完整的ACID事务特性

  • Atomicity(原子性):事务操作要么全部完成,要么全部回滚。

  • Consistency(一致性):事务开始和结束时,数据库必须处于一致状态。

  • Isolation(隔离性):通过多版本并发控制(MVCC)保证事务间隔离。

  • Durability(持久性):事务提交后数据通过Redo日志保证永久保存。

关键机制

  1. Undo日志

    • 用于回滚事务和实现MVCC。

    • Undo信息通常存储在专用Undo页中,并通过聚簇索引维护回滚链表。

  2. Redo日志

    • 用于崩溃恢复,保证已提交事务的数据持久性。

    • Redo日志记录物理页的修改操作,而非行级别逻辑变更。

  3. 锁机制

    • 支持行锁(Record Lock)、间隙锁(Gap Lock)、意向锁(Intention Lock)等。

    • 行锁粒度小,支持高并发写操作。

1.3 聚簇索引与辅助索引

InnoDB表使用**聚簇索引(Clustered Index)**组织表数据:

  • 聚簇索引叶子节点存储完整行数据,主键顺序决定行存储顺序。

  • 辅助索引(Secondary Index)的叶子节点只存储主键值 + 索引列

  • 查询时:

    • 使用主键直接定位页和行。

    • 使用辅助索引需要先通过索引查到主键,再回聚簇索引查数据。

⚡ 类比说明:聚簇索引是“以主键为地址的档案柜”,辅助索引是“目录索引表”,通过目录找到主柜位置。

1.4 数据一致性与MVCC原理

InnoDB通过**多版本并发控制(MVCC)**实现一致性读:

  • 每行记录包含两个隐藏列:

    • DB_TRX_ID:最近修改该行的事务ID。

    • DB_ROLL_PTR:指向Undo日志链表的指针。

  • 查询操作通过比较事务ID和Undo日志,生成快照读,保证读取一致性。

⚡ 类比说明:MVCC就像“时间机器”,可以让事务看到历史版本的数据而不阻塞其他事务。

1.5 配置与性能优化提示

  • innodb_file_per_table

    • 开启独立表空间可以减少碎片化,便于表级恢复和压缩。

  • innodb_page_size

    • 对大数据量表,可将页大小调整为32KB或64KB,提高顺序扫描效率。

  • 事务日志配置

    • Redo日志文件大小和缓冲池大小应根据负载优化,以减少磁盘I/O。

💡 小结

本章梳理了InnoDB的宏观存储架构和核心概念:表空间 → 页 → 行 → 字段,以及事务、MVCC、索引对存储和查询的影响。理解这些基础,对于后续深入分析页结构和行格式至关重要。

2. 页结构详解

InnoDB中,**页(Page)**是最小的存储与I/O单位,每个页默认大小为16KB(可通过innodb_page_size修改)。页不仅存储数据,还维护索引信息、事务日志指针等元数据。理解页结构,是分析行格式和性能优化的前提。

2.1 页的分类与作用

InnoDB页按功能可分为以下几类:

  1. 数据页(Index Leaf Page)

    • 存储B+树叶子节点上的完整行数据。

    • 聚簇索引的叶子节点即为数据页。

  2. 非叶子节点页(Index Internal Page)

    • 存储索引键和子页指针,不包含完整行数据。

    • 用于B+树导航,快速定位数据页。

  3. Undo页(Undo Log Page)

    • 存储事务回滚信息,用于回滚事务和MVCC版本管理。

  4. BLOB/Overflow页(Overflow Page)

    • 存储大字段(如VARCHAR、TEXT、BLOB)超过768字节的数据。

    • 主数据页仅存储指针,实际数据存放在BLOB页。

  5. 系统页(System Page)

    • 包括FSP header、insert buffer bitmap等,维护表空间元信息。

⚡ 类比说明:页就像“硬盘上的小房间”,每类页承担不同功能:有的存数据,有的存索引,有的存历史记录,有的存大对象。


2.2 页头/页尾字段解析

每个页都包含页头(File Header)页尾(File Trailer),用于维护页的状态和完整性。

2.2.1 页头(Page Header)

页头通常位于页开头,占用约38~56字节,主要字段包括:

字段名大小作用
PAGE_N_DIR_SLOTS2B页目录槽数
PAGE_N_HEAP2B页中记录数
PAGE_FREE2B空闲空间指针
PAGE_LSN8B最新修改日志序列号(Log Sequence Number)
PAGE_PREV/PAGE_NEXT4B各链接同类页形成双向链表
PAGE_TYPE2B页类型标识(数据页/索引页/Undo页等)

⚡ LSN(Log Sequence Number)用于Crash Recovery,标记页的最新修改状态。

2.2.2 页尾(Page Trailer)

页尾用于完整性校验,主要字段包括:

字段名大小作用
PAGE_FILE_FLUSH_LSN8B页写磁盘的日志序列号
PAGE_SPACE_ID4B所属表空间ID
校验码(Checksum)4B数据完整性验证

⚡ 类比说明:页头像“房间门口的登记簿”,页尾像“安全锁”,保证房间被正确使用。


2.3 用户记录存储机制

页中的行记录存储有严格布局,主要组成部分:

  1. 记录头(Record Header)

    • 包含前向/后向指针、标记位、NULL位信息。

    • COMPACT格式记录头为5字节,REDUNDANT为7~9字节。

  2. 字段数据

    • 固定长度字段:按声明顺序存储,长度固定。

    • 变长字段:存储长度+数据,COMPACT格式采用逆序存储长度列表。

    • NULL标志位:用bit位表示哪些字段为NULL。

  3. 页目录(Page Directory)

    • 页内有一个槽位数组,指向记录链表的起始位置。

    • 插入、删除、更新操作通过页目录快速定位记录。

  4. 空闲空间管理

    • PAGE_FREE指向页中可用空间的起始位置。

    • 插入新记录时,InnoDB先检查连续空间,再考虑碎片整理。

2.3.1 记录链表伪代码
// 遍历页内记录链表
Record* rec = page->first_record;
while (rec != NULL) {process_record(rec);          // 处理记录rec = rec->next_record;       // 通过next_record指针遍历
}

⚡ 注释:first_record指向页内第一个记录,next_record为记录头中的偏移字段,形成单向链表,页目录用于加速定位。


2.4 页分裂与碎片化

  • 当页插入新记录后空间不足,会触发页分裂(Page Split)

    • 将页拆成两个页,部分记录移动到新页。

    • B+树非叶子节点需要调整索引指针。

  • 高并发写入和大字段更新会导致页碎片化,降低存储密度和查询性能。

  • 优化方法:定期使用OPTIMIZE TABLE整理碎片,或者选择DYNAMIC/COMPRESSED行格式存储大字段。


💡 小结

本章重点解析了InnoDB页的内部结构:

  1. 页的分类和功能:数据页、索引页、Undo页、BLOB页。

  2. 页头/页尾字段:LSN、页类型、校验码保证完整性。

  3. 用户记录布局:记录头、字段数据、页目录、链表指针。

  4. 页分裂与碎片化:影响存储密度和查询性能,需优化。

理解页结构后,才能深入分析行格式的存储细节,如COMPACT、REDUNDANT、DYNAMIC对页内记录的布局差异。

3. 行格式深度解析

InnoDB页中的每条记录都按照**行格式(Row Format)**存储。行格式决定了记录头长度、变长字段存储方式、NULL标志位位置、溢出列处理策略等,直接影响存储密度和查询性能。


3.1 COMPACT行格式

COMPACT是MySQL 5.0之后默认行格式,相比REDUNDANT存储更加紧凑,提高页利用率。

3.1.1 记录头与字段存储
  • 记录头:5字节

    • info_bits:1字节,标记删除/外部列等状态

    • n_owned:1字节,继承自REDUNDANT格式的遗留标志

    • next_record:2字节,指向下一条记录偏移

    • heap_no:1字节,记录在页目录中的槽位编号

  • 固定长度字段:顺序存储,无额外长度信息

  • 变长字段

    • 长度列表:逆序存储在记录尾部,每个字段长度占1或2字节

    • NULL标志位:独立存储在记录尾部,每个字段占1 bit

⚡ 类比说明:固定字段像“箱子里的固定格子”,变长字段像“伸缩格子”,通过尾部长度列表快速定位数据。

3.1.2 SQL建表示例
CREATE TABLE t_compact (id INT NOT NULL,name VARCHAR(100),description TEXT,created_at DATETIME,PRIMARY KEY (id)
) ROW_FORMAT=COMPACT;
3.1.3 COMPACT格式伪代码解析
// 解析COMPACT行记录
Record* rec = page->first_record;
while (rec != NULL) {int fixed_offset = rec->start;                   // 固定字段起始位置process_fixed_fields(rec, fixed_offset);int var_offset = rec->end - var_len_list_size;   // 变长字段从尾部倒序解析process_var_fields(rec, var_offset, var_len_list);rec = rec->next_record;
}
3.1.4 性能特点
  • 优点:页利用率高,查询效率较好

  • 缺点:大字段仍可能导致BLOB溢出,需要Off Page存储


3.2 REDUNDANT行格式

REDUNDANT是早期MySQL默认行格式,兼容性好,但存储空间浪费较多。

  • 记录头:7~9字节

    • next_recordprev_record双向链表指针

    • 每个变长字段都存储长度信息

  • NULL标志:1字节表示1个字段是否为NULL,不支持bit压缩

  • 特点

    • 存储密度低

    • 页面碎片化严重

    • 大字段处理类似COMPACT,但尾部长度列表不紧凑

SQL建表示例
CREATE TABLE t_redundant (id INT NOT NULL,name VARCHAR(100),description TEXT,created_at DATETIME,PRIMARY KEY (id)
) ROW_FORMAT=REDUNDANT;

⚡ 对比:COMPACT通过逆序长度列表和bit压缩NULL位节省了约1~2字节/字段。


3.3 溢出列处理(Off Page BLOB)

当字段过大(如VARCHAR/TEXT/BLOB超过768字节):

  1. 主数据页只存储20字节指针和部分前缀数据

  2. 超过部分存储在BLOB页(Overflow Page)

  3. 页中记录头info_bits标记该列为外部列

伪代码示例
if (field_length > 768) {store_prefix_in_page(field);store_remaining_in_blob_page(field);rec->info_bits |= EXTERNAL_FIELD_FLAG;
}

⚡ 类比说明:大字段就像“家具拆分存放”,主房间只留指针和小件,主房间保持整洁。


3.4 DYNAMIC与COMPRESSED行格式

3.4.1 DYNAMIC行格式
  • 与COMPACT类似,但大字段(BLOB/TEXT/VARCHAR)默认存放在外部页

  • 页内只存储指针,减少页内碎片化

  • innodb_file_per_table=ON 时效果最佳

    CREATE TABLE t_dynamic (id INT NOT NULL,name VARCHAR(500),content TEXT,created_at DATETIME,PRIMARY KEY(id)
    ) ROW_FORMAT=DYNAMIC;
    

  • 优点:页利用率高,适合大字段场景

  • 缺点:访问大字段需额外I/O

3.4.2 COMPRESSED行格式
  • 在DYNAMIC基础上增加页压缩(zlib算法)

  • 参数:

    • innodb_compression_level=6(默认)

    • 压缩后页仍为16KB逻辑大小

  • 适用于存储密集型表,减少磁盘占用

  • 访问时需解压缩,增加CPU开销


3.5 COMPACT vs DYNAMIC 性能对比

指标COMPACTDYNAMIC
页利用率较高更高,特别是大字段
BLOB/TEXT处理部分溢出全部外部存储
插入/更新效率略低,外部页I/O增加
OLTP适用性优秀中等,适合大对象

⚡ 建议:OLTP场景优先使用COMPACT,大字段场景使用DYNAMIC或COMPRESSED。


3.6 源码片段解析(row0format.cc)

以COMPACT行解析为例:

/** * 解析COMPACT记录头* @rec: 页内记录指针*/
void row_parse_compact(const rec_t* rec) {const byte* field_ptr = rec->data;for (int i = 0; i < rec->n_fields; i++) {if (rec->is_varlen(i)) {int len = read_var_len(rec, i);  // 从尾部长度列表读取process_var_field(field_ptr, len);field_ptr += len;} else {process_fixed_field(field_ptr, rec->field_len(i));field_ptr += rec->field_len(i);}}
}

注释:read_var_len()根据记录尾部长度列表倒序读取变长字段长度;固定字段按顺序存储。


💡 小结

  1. COMPACT:紧凑、尾部长度列表、bit压缩NULL位

  2. REDUNDANT:冗余、每字段存长度、空间浪费

  3. DYNAMIC:大字段外部存储,页内保持紧凑

  4. COMPRESSED:在DYNAMIC基础上增加页压缩,节省磁盘空间

行格式选择直接影响页利用率、查询性能和BLOB访问开销。理解行格式是分析InnoDB存储优化的核心。

http://www.xdnf.cn/news/18602.html

相关文章:

  • 服务发现实例和服务实例是不同的
  • reactive 核心要点
  • Unreal Engine UPrimitiveComponent
  • 数据分析编程第二步: 最简单的数据分析尝试
  • day58 拓扑排序 (kama117. 软件构建) dijkstra(朴素版)(kama47. 参加科学大会)
  • 无人机电机与螺旋桨的匹配原理及方法(一)
  • JavaSSM框架从入门到精通!第三天(MyBatis(二))!
  • Python训练营打卡Day40-简单CNN
  • 【51单片机数码管字符左移】2022-11-11
  • 如何低门槛自制Zigbee 3.0温湿度计?涂鸦上新开发包,开箱即用、完全开源
  • 开源AI编程工具Kilo Code的深度分析:与Cline和Roo Code的全面对比
  • Tiger任务管理系统-13
  • 【jar包启动,每天生成一个日志文件】
  • Unity UnityWebRequest高级操作
  • Ubuntu部署K8S集群
  • Jmeter+Jenkins接口压力测试持续集成
  • 【motion】基于标签重合度的匹配算法1:原理
  • 3D打印小批量低成本打印玩具工艺品模型-中科米堆CASAIM
  • 字节Seed-OSS开源,不卷参数卷脑子
  • 从零开始搭 Linux 环境:VMware 下 CentOS 7 的安装与配置全流程(附图解)
  • 如何修复“DNS服务器未响应”错误
  • AP服务发现PRS_SOMEIPSD_00160的解析
  • 开源版CRM客户关系管理系统源码包+搭建部署教程
  • 深度学习入门详解:从神经网络到实践应用
  • vggt复现
  • 正点原子【第四期】Linux之驱动开发学习笔记-2.1LED灯驱动实验(直接操作寄存器)
  • Mysql InnoDB 底层架构设计、功能、原理、源码系列合集【四、事务引擎核心 - MVCC与锁机制】
  • 【AI应用】向量数据库Milvus详细命令
  • 找不到vcruntime140_1.dll 无法执行的故障要怎么搞?解决方法分享
  • MiniCPM-V4.0开源并上线魔乐社区,多模态能力进化,手机可用,还有最全CookBook!