深入理解 MySQL InnoDB:从表空间、页到 Redo Log、Binlog 和慢查询日志
- 深入理解 MySQL InnoDB:从表空间、页到 Redo Log、Binlog 和慢查询日志
- 前言
- 一、InnoDB 的整体存储结构
- 二、InnoDB 逻辑存储结构
- 1. Tablespace:表空间
- 三、Segment:段
- 四、Extent:区
- 五、Page:InnoDB 最重要的存储单位
- 1. 为什么 Page 很重要?
- 2. 常见 Page 类型
- 六、Row:最终的数据记录
- 七、InnoDB 的物理文件
- 八、数据文件:ibdata 与 .ibd
- 九、Redo Log:保证事务持久性的关键日志
- 1. 为什么需要 Redo Log?
- 十、Redo Log 和 Binlog 到底有什么区别?
- 十一、Undo:保存数据修改前的信息
- 1. 事务回滚
- 2. MVCC
- 十二、MySQL 配置文件
- 十三、常见 MySQL 配置参数
- 动态参数与静态参数
- 十四、Error Log:排查 MySQL 故障的第一现场
- 十五、Binlog:MySQL 数据恢复和复制的核心日志
- 十六、Binlog 有什么作用?
- 1. 主从复制
- 2. 数据恢复
- 十七、Binlog 的三种格式
- 1. STATEMENT
- 2. ROW
- 3. MIXED
- 十八、如何查看 Binlog 是否开启?
- 十九、Slow Query Log:定位慢 SQL 的重要工具
- 1. 临时开启慢查询日志
- 2. 如何验证慢查询日志?
- 二十、mysqldumpslow:快速分析慢日志
- 二十一、General Log:记录几乎所有数据库操作
- 二十二、Relay Log:主从复制中的中继日志
- 二十三、PID 文件
- 二十四、Socket 文件
- 二十五、表结构元数据发生了什么变化?
- 二十六、MySQL 8.0 学习时一定要注意“版本问题”
- 二十七、把所有核心日志放在一起理解
- 二十八、InnoDB 存储体系全景图
- 总结
深入理解 MySQL InnoDB:从表空间、页到 Redo Log、Binlog 和慢查询日志
前言
InnoDB 是 MySQL 最常用、也是默认的事务型存储引擎。
平时写 SQL 时,我们看到的通常只是数据库、表、字段和索引,例如:
CREATETABLEuser(idBIGINTPRIMARYKEY,nameVARCHAR(50));但一条数据真正落到磁盘后,并不是简单地“保存到一个表文件里”。
在 InnoDB 内部,数据会经过一套完整的存储体系:
表空间 ↓ 段 ↓ 区 ↓ 页 ↓ 行与此同时,MySQL 还会维护多种文件与日志:
.ibd 数据文件 redo log undo log binlog error log slow query log general log relay log PID 文件 Socket 文件这些组件共同完成数据存储、事务恢复、主从复制、故障排查以及 SQL 性能分析。
本文就从底层存储结构开始,系统梳理 InnoDB 的核心组成。
一、InnoDB 的整体存储结构
InnoDB 的存储结构可以分成两个角度理解:
InnoDB ├── 逻辑存储结构 │ ├── Tablespace │ ├── Segment │ ├── Extent │ ├── Page │ └── Row │ └── 物理存储结构 ├── 数据文件 ├── Redo Log ├── Undo ├── 配置文件 ├── 各类运行日志 └── 其他辅助文件逻辑存储结构描述的是:
InnoDB 如何组织和管理数据。
物理存储结构描述的则是:
这些数据最终以什么文件形式存在磁盘上。
理解 InnoDB,最好先从逻辑层开始。
二、InnoDB 逻辑存储结构
1. Tablespace:表空间
表空间可以理解为 InnoDB 逻辑存储结构中的最高层。
InnoDB 的各种数据最终都存储在不同类型的表空间中。
早期 InnoDB 经常使用共享系统表空间,例如:
ibdata1可以在 MySQL 数据目录中看到类似文件:
ls-lh/usr/local/mysql/data/如果使用独立表空间,则每张 InnoDB 表通常拥有自己的.ibd文件。
与之相关的重要参数是:
SHOWVARIABLESLIKE'innodb_file_per_table';常见结果:
innodb_file_per_table ON开启独立表空间之后,一张表的数据和索引可以保存在自己的.ibd文件中。
例如:
demo/ ├── user.ibd ├── orders.ibd └── product.ibd不过要注意:
独立表空间并不意味着 InnoDB 的所有数据都会进入
.ibd文件。
例如系统级信息、Undo、部分内部结构等可能存储在其他专用表空间或系统区域中。
三、Segment:段
表空间内部继续划分为 Segment,也就是“段”。
典型的 Segment 包括:
数据段 索引段 回滚段对于 InnoDB 来说尤其值得注意的一点是:
InnoDB 是索引组织表(Index Organized Table)。
也就是说,InnoDB 表中的数据本身就是按照索引结构组织的。
对于聚簇索引而言:
索引 ≈ 数据组织结构因此,不能完全把“索引文件”和“数据文件”理解成两套互不相关的东西。
InnoDB 的主键索引叶子节点本身就保存了完整的行数据。
四、Extent:区
Segment 继续向下划分,就是 Extent,也就是“区”。
Extent 是由一组连续的 Page 组成的空间分配单位。
在默认 16KB Page 的情况下,一个 Extent 通常包含:
64 个 Page因此:
64 × 16KB = 1024KB = 1MB也就是说,在典型配置下:
1 Extent = 1MB可以理解为:
Tablespace ↓ Segment ↓ Extent(约 1MB) ↓ Page为什么不直接一页一页申请空间?
因为如果数据库频繁向操作系统申请极小的存储空间,会增加管理成本。
采用 Extent,可以一次申请一组连续页面,有利于提高空间管理和顺序访问效率。
五、Page:InnoDB 最重要的存储单位
如果只记住一个概念,那么一定要记住:
Page 是 InnoDB 最基本、最核心的磁盘存储单位。
默认情况下,InnoDB Page 大小通常为:
16KB可以查看:
SHOWVARIABLESLIKE'innodb_page_size';典型结果:
innodb_page_size 1638416384 Byte 正好是:
16KB1. 为什么 Page 很重要?
当 MySQL 查询一条记录时,并不是只从磁盘读取那几十个字节的数据。
磁盘和内存之间的数据交换通常是以 Page 为基本单位进行的。
简单理解:
磁盘 ↓ 16KB Page ↓ Buffer Pool ↓ SQL 使用数据所以在分析 MySQL:
- 索引
- Buffer Pool
- 随机 IO
- 顺序 IO
- 页分裂
- 页命中率
这些问题时,Page 都是基础概念。
2. 常见 Page 类型
InnoDB 中并不是所有 Page 都用来存放普通数据。
常见页面包括:
数据页 Undo 页 系统页 事务系统页 插入缓冲相关页面 大对象页面 压缩大对象页面其中实际开发中最常接触的是:
B+Tree 数据页InnoDB 索引树中的节点就是由一个个 Page 构成的。
六、Row:最终的数据记录
Page 再往下,就是 Row,也就是行。
InnoDB 是一个:
Row-Oriented Storage Engine即面向行的存储引擎。
例如:
INSERTINTOuserVALUES(1,'Tom',18);最终数据会以行记录的形式存放在数据页中。
从宏观到微观,可以形成完整关系:
Tablespace ↓ Segment ↓ Extent ↓ Page ↓ Row这条关系是理解 InnoDB 存储结构的核心。
七、InnoDB 的物理文件
理解完逻辑结构,再来看数据真正落到操作系统后,会出现哪些文件。
八、数据文件:ibdata 与 .ibd
InnoDB 最直接的数据文件主要可以分为:
系统表空间文件 独立表空间文件传统的系统表空间文件常见:
ibdata1独立表空间文件则通常是:
表名.ibd例如:
demo/ ├── user.ibd ├── orders.ibd └── goods.ibd在开启:
innodb_file_per_table之后,每张 InnoDB 表通常会建立自己的独立表空间文件。
这使得单表空间管理更加灵活。
九、Redo Log:保证事务持久性的关键日志
Redo Log 是理解 InnoDB 必须掌握的日志。
它属于:
InnoDB 存储引擎层核心目标是:
保证数据库发生异常宕机之后,已经提交或需要恢复的修改能够重新恢复出来。
1. 为什么需要 Redo Log?
假设执行:
UPDATEaccountSETmoney=money-100WHEREid=1;如果每次事务提交都必须立刻把所有修改过的数据页随机写入磁盘,那么性能会非常差。
因为数据页可能分布在磁盘不同位置。
InnoDB 会利用 Redo Log,将随机的数据页修改转变为更适合持久化的日志写入。
可以简单理解成:
修改数据 ↓ Buffer Pool 中的 Page 被修改 ↓ 产生 Redo ↓ Redo 持久化 ↓ 脏页之后再刷入数据文件如果数据库突然宕机:
数据文件可能还没完全写入但只要 Redo 中记录了必要的修改信息,就可以在数据库重新启动时进行恢复。
十、Redo Log 和 Binlog 到底有什么区别?
这是 MySQL 面试中非常高频的问题。
虽然两者都叫“日志”,但完全不是一回事。
可以从几个维度理解。
| 对比项 | Redo Log | Binlog |
|---|---|---|
| 所属层次 | InnoDB 存储引擎层 | MySQL Server 层 |
| 主要用途 | 崩溃恢复 | 复制、数据恢复 |
| 内容特点 | 偏物理变化 | 逻辑事件/行变化 |
| 写入方式 | 持续写入 | 按事务记录 |
| 使用方式 | 循环使用的日志空间机制 | 持续生成新的日志文件 |
| 典型场景 | Crash Recovery | 主从复制、时间点恢复 |
可以用一句话记忆:
Redo Log:保证数据库自己“摔倒还能爬起来” Binlog:记录数据库“做过什么”十一、Undo:保存数据修改前的信息
与 Redo 对应的另一个重要概念就是 Undo。
Redo 更关注:
如何把修改重新做一遍Undo 则更关注:
如何获得修改前的数据版本当一条记录发生修改时,InnoDB 会产生相应的 Undo 信息。
Undo 在两个场景中非常重要:
事务回滚 MVCC1. 事务回滚
例如:
BEGIN;UPDATEaccountSETmoney=0WHEREid=1;ROLLBACK;执行ROLLBACK后需要将数据恢复到修改之前的状态。
这时就需要借助 Undo 信息。
2. MVCC
当一个事务修改某条记录时,另一个事务可能仍然需要读取它之前的版本。
这就是:
Multi-Version Concurrency Control MVCCInnoDB 可以借助 Undo 中保存的历史版本构造一致性读需要的数据。
因此:
Redo → 重做 Undo → 撤销 / 历史版本两者的作用完全不同。
十二、MySQL 配置文件
MySQL 启动时需要读取配置文件。
在 Linux 环境下,常见配置文件位置可能包括:
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf可以使用:
mysql--help|grepmy.cnf查看配置文件搜索路径。
如果需要明确指定某个配置文件,可以在启动时使用相应参数,例如:
mysqld --defaults-file=/etc/my3306.cnf十三、常见 MySQL 配置参数
MySQL 配置通常分为服务端和客户端。
例如服务端:
[mysqld] port=3306 basedir=/usr/local/mysql datadir=/usr/local/mysql/data客户端:
[client] port=3306 default-character-set=utf8mb4常见参数包括:
port basedir datadir socket pid-file character-set-server lower_case_table_names default-storage-engine log-error动态参数与静态参数
MySQL 参数还可以从是否支持在线修改的角度分类。
一部分参数可以动态修改,例如:
SETGLOBAL参数名=值;或者:
SETSESSION参数名=值;两者区别是:
GLOBAL ↓ 影响之后建立的连接或全局环境 SESSION ↓ 只影响当前连接而部分静态参数通常需要:
修改配置文件 + 重启 MySQL才能生效。
十四、Error Log:排查 MySQL 故障的第一现场
Error Log,即错误日志。
它会记录 MySQL:
启动 运行 异常 关闭 故障等过程中产生的重要信息。
可以查看相关配置:
SHOWVARIABLESLIKE'log_error';当出现:
MySQL 启动失败 表空间文件丢失 权限错误 配置错误 InnoDB 恢复异常这类问题时,第一个应该检查的通常就是 Error Log。
因此实际运维时可以形成一个习惯:
MySQL 出现异常,先查错误日志。
十五、Binlog:MySQL 数据恢复和复制的核心日志
Binlog 全称:
Binary Log也就是二进制日志。
它由 MySQL Server 层产生。
Binlog 主要记录:
对数据库数据造成修改的事件。
例如:
INSERTUPDATEDELETECREATETABLEALTERTABLE而类似:
SELECTSHOW通常不会作为普通数据修改事件记录进去。
十六、Binlog 有什么作用?
Binlog 最重要的两个作用是:
1. 主从复制 2. 数据恢复1. 主从复制
主库执行:
UPDATEuserSETname='Tom'WHEREid=1;之后变化被记录到 Binlog。
从库获取主库 Binlog,再重放其中的事件:
Master ↓ Binlog ↓ Replica ↓ Relay Log ↓ 重放最终实现数据同步。
2. 数据恢复
如果误删了数据:
DELETEFROMuser;只要备份和 Binlog 策略合理,就可以通过:
全量备份 + Binlog实现时间点恢复。
这也是生产数据库必须认真规划 Binlog 的原因之一。
十七、Binlog 的三种格式
Binlog 经典的三种日志格式分别为:
STATEMENT ROW MIXED1. STATEMENT
STATEMENT 记录执行过的 SQL。
例如:
UPDATEuserSETmoney=money+100WHEREid=1;Binlog 中主要记录这条 SQL。
优点:
日志量相对较小缺点是某些依赖上下文、随机函数或者环境差异的 SQL 可能产生复制一致性问题。
2. ROW
ROW 模式重点记录:
哪些行发生了怎样的变化。
它不依赖从库重新“理解”原 SQL 的业务语义。
优点:
复制更加可靠 数据一致性更好缺点:
大量数据更新时 Binlog 可能明显增大例如:
UPDATEuserSETstatus=1;如果修改 100 万行,ROW 模式需要记录大量行变化。
3. MIXED
MIXED 可以理解为:
STATEMENT + ROWMySQL 根据具体 SQL 情况选择适合的日志形式。
十八、如何查看 Binlog 是否开启?
可以使用:
SHOWVARIABLESLIKE'%log_bin%';重点关注:
log_bin log_bin_basename log_bin_index含义分别可以理解为:
log_bin 是否启用 Binlog log_bin_basename Binlog 文件基础路径 log_bin_index Binlog 索引文件查看当前 Binlog 状态时,也可以使用对应版本支持的状态命令。
查看具体日志事件,例如:
SHOWBINLOG EVENTSIN'binlog.000010';在服务器命令行还可以利用:
mysqlbinlog binlog.000010解析 Binlog。
十九、Slow Query Log:定位慢 SQL 的重要工具
对于数据库性能优化来说,慢查询日志非常重要。
它会记录执行时间超过指定阈值的 SQL。
首先查看:
SHOWVARIABLESLIKE'%slow_query%';常见变量包括:
slow_query_log slow_query_log_file查看慢查询阈值:
SHOWVARIABLESLIKE'long_query_time';例如:
long_query_time = 2意味着执行时间达到相应条件的 SQL 可以被纳入慢查询分析范围。
1. 临时开启慢查询日志
例如:
SETGLOBALslow_query_log=ON;调整慢查询阈值:
SETGLOBALlong_query_time=2;如果希望长期生效,通常应该写入 MySQL 配置文件。
例如:
[mysqld] slow_query_log=ON slow_query_log_file=/usr/local/mysql/data/mysql-slow.log long_query_time=2修改后按照实际环境使配置生效。
2. 如何验证慢查询日志?
可以人为执行一条耗时 SQL,例如:
SELECTSLEEP(3);然后检查慢日志。
日志中通常能够看到:
执行时间 用户 主机 Query_time Lock_time Rows_sent Rows_examined SQL这些数据对 SQL 性能诊断非常有帮助。
例如:
Query_time 很大 Rows_examined 非常大 Rows_sent 很小往往意味着:
数据库扫描了大量数据,但真正返回的数据非常少。
这种 SQL 就值得重点检查索引设计和执行计划。
二十、mysqldumpslow:快速分析慢日志
当慢查询日志非常大时,人工查看效率很低。
可以使用:
mysqldumpslow mysql-slow.log进行初步聚合分析。
在生产环境中,慢查询日志还经常会结合:
pt-query-digest Performance Schema EXPLAIN EXPLAIN ANALYZE进行进一步分析。
完整的 SQL 优化链路通常是:
二十一、General Log:记录几乎所有数据库操作
General Log 又叫:
全量日志 / 通用查询日志它可以记录连接到 MySQL 后执行的大量操作,包括:
SELECTSHOWINSERTUPDATEDELETE查看配置:
SHOWVARIABLESLIKE'%general_log%';开启:
SETGLOBALgeneral_log=ON;General Log 在问题诊断时很有价值,但它的日志量可能非常大。
因此生产环境通常:
不建议长时间无目的开启 General Log。
否则容易产生:
大量磁盘 IO 日志快速膨胀 额外性能开销更适合临时排查问题。
二十二、Relay Log:主从复制中的中继日志
Relay Log 主要出现在 MySQL 复制体系中的从库一侧。
传统复制流程可以抽象成:
因此:
Binlog:主库产生 Relay Log:从库复制过程中使用两者不能混为一谈。
可以查看与 Relay Log 相关的参数,例如:
SHOWVARIABLESLIKE'%relay%';其中可能包含:
relay_log relay_log_index relay_log_purge relay_log_recovery等配置。
二十三、PID 文件
MySQL Server 启动之后,本质上也是操作系统中的一个进程。
系统需要记录:
mysqld 的进程 ID这通常通过 PID 文件实现。
可以查看:
SHOWVARIABLESLIKE'%pid%';例如:
pid_file对应文件中通常保存一个数字:
44764这个数字就是 MySQL Server 对应进程的 PID。
PID 文件对于:
服务管理 进程控制 启动停止 状态检查都有一定作用。
二十四、Socket 文件
Linux/Unix 系统下,客户端和本机 MySQL Server 之间除了 TCP/IP,还可以通过 Unix Socket 通信。
查看 Socket 文件位置:
SHOWVARIABLESLIKE'socket';常见值类似:
/tmp/mysql.sock例如本地执行:
mysql-uroot-p某些情况下客户端默认就是通过:
Unix Socket连接 MySQL。
因此当遇到:
Can't connect to local MySQL server through socket这一类错误时,就应该检查:
MySQL 是否启动 socket 文件是否存在 客户端和服务端 socket 路径是否一致 文件权限是否正常二十五、表结构元数据发生了什么变化?
在较早版本的 MySQL 中,表结构信息会和.frm文件联系在一起。
例如过去一张表可能对应:
table.frm table.ibd其中.frm用于保存表结构定义。
但进入 MySQL 8.0 后,元数据管理方式发生了重大变化。
MySQL 8 使用事务型数据字典,将大量数据库对象的元数据信息统一管理起来,不再继续依赖传统.frm文件作为普通表定义的核心存储方式。
因此学习 MySQL 文件结构时必须注意版本差异。
二十六、MySQL 8.0 学习时一定要注意“版本问题”
MySQL 的底层实现一直在演进。
很多早期资料中的:
文件名 默认值 系统变量 日志管理方式 数据字典结构 复制术语在新的 MySQL 版本中都可能发生变化。
尤其涉及:
Redo Log 参数 .frm 文件 复制相关命令 默认认证插件 默认字符集时,更应该确认自己当前使用的版本。
可以先执行:
SELECTVERSION();再针对当前版本查看:
SHOWVARIABLES;避免直接照搬其他版本的配置。
对于生产环境,更应该以实际版本的官方文档和SHOW VARIABLES结果为准。
二十七、把所有核心日志放在一起理解
最后将 MySQL 中几个最容易混淆的日志统一梳理一下。
| 日志 | 主要作用 | 典型使用场景 |
|---|---|---|
| Redo Log | 保证事务持久性、崩溃恢复 | MySQL 异常宕机恢复 |
| Undo | 回滚、历史版本 | MVCC、事务回滚 |
| Binlog | 记录数据变更事件 | 主从复制、数据恢复 |
| Relay Log | 保存从主库获取的复制事件 | 从库复制 |
| Error Log | 记录启动运行错误 | 故障排查 |
| Slow Query Log | 记录慢 SQL | SQL 性能优化 |
| General Log | 记录数据库操作 | 临时问题诊断 |
如果觉得日志很多,可以这样记:
Redo → 数据库崩了怎么恢复 Undo → 数据改了怎么撤回、怎么看旧版本 Binlog → 数据库曾经做过哪些修改 Relay Log → 从库准备执行哪些复制事件 Error Log → MySQL 到底哪里报错了 Slow Log → 到底是哪条 SQL 慢 General Log → MySQL 最近执行了什么二十八、InnoDB 存储体系全景图
将本文内容串起来,可以形成这样一个整体认识:
同时外围还存在:
Error Log Slow Query Log General Log Relay Log PID Socket 配置文件共同构成一个完整的 MySQL 运行环境。
总结
理解 InnoDB,不能只停留在:
SELECTINSERTUPDATEDELETE更重要的是理解 SQL 背后发生了什么。
从存储层面来看:
Tablespace → Segment → Extent → Page → Row其中Page 是 InnoDB 最核心的存储管理单位之一。
从事务机制来看:
Redo Log → 保证崩溃恢复和持久性 Undo → 支持事务回滚和 MVCC从 MySQL Server 层来看:
Binlog → 支持复制和数据恢复从运维角度来看:
Error Log → 排查异常 Slow Query Log → 定位慢 SQL General Log → 临时追踪数据库操作 Relay Log → 支撑复制真正理解这些组件之后,再学习:
Buffer Pool B+Tree 索引 MVCC 事务隔离 脏页刷新 Checkpoint 主从复制 SQL 优化就会容易很多。
因为它们本质上都建立在 InnoDB 的存储结构和日志体系之上。
若有转载,请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/164188642