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

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.dtcms.com/a/346206.html

相关文章:

  • windows 帮我写一个nginx的配置,端口是9999,静态资源的路径是D:\upload
  • 企业架构之微服务应用架构
  • 深入理解底层通信协议和应用层协议的区别
  • Java Stream常见函数与应用案例
  • 大模型应用发展与Agent前沿技术趋势(下)
  • Debezium导致线上PostgreSQL数据库磁盘日志飙升处理方案
  • Unreal Engine ATriggerVolume
  • java 海报、图片合成
  • 蓝牙部分解析和代码建构
  • SSH如何访问只有没有公网IP的云服务器
  • loss 基本稳定,acc 一直抖动,如何优化?
  • assetbuddle hash 比对
  • 【计算机网络】 IPV4和IPV6区别
  • JSON学习和应用demo
  • 每日算法题【链表】:移除链表元素、反转链表
  • 嵌入式第三十五课!!Linux下的网络编程
  • 非标机械设备工厂,一般会遇到哪些问题
  • Linux服务器查看启动服务的5种方法
  • 基于RBAC的权限控制:从表设计到接口实现全指南
  • Beszel 服务器监控平台使用教程
  • JVM虚拟机
  • Leetcode—1683. 无效的推文【简单】
  • 网络与信息安全有哪些岗位:(7)等级保护测评师
  • tensorflow-gpu 2.7下的tensorboard与profiler插件版本问题
  • 第九章 Leaflet 实战:多边形绘制工具开发与面积实时计算(含双击报错修复方案)
  • Qt QML实现 无边框圆角窗口拖动(附窗口控制按钮)
  • RAG初筛方案实例验证-多种BM25方案
  • 类器官培养基系列,助力高效医学研究
  • Navicat连接MySQL-出现1045无法连接问题
  • AI实验管理神器:WandB全功能解析