逻辑存储结构
表空间(ibd文件)
一个MySQL实例可对应多个表空间,用于存储记录、索引等数据。
段
- 分为数据段(叶子节点段)、索引段(非叶子节点段)、回滚段 。
- InnoDB是索引组织表,数据段即B+树叶子节点,索引段即B+树非叶子节点,段用于管理多个区。
区(Extent)
- 表空间的单元结构,每个区大小为1M 。
- InnoDB存储引擎页大小默认16K,一个区有64个连续的页。
页(Page)
- InnoDB存储引擎磁盘管理最小单元,默认大小16KB 。
- 为保证页连续性,每次从磁盘申请4 - 5个区。
行(Row)
- InnoDB数据按行存放 。
- Trx_id:记录改动时,对应事务id赋值给该隐藏列。
- Roll_pointer:记录改动时,旧版本写入undo日志,该隐藏列像指针,可找到修改前信息。
架构
内存架构
Buffer Pool
概述
Buffer Pool是内存区域,用于缓存磁盘上常操作的真实数据。执行增删改查时,先操作缓存数据(无则从磁盘加载并缓存),再定时刷新到磁盘,减少磁盘I/O。
Buffer Pool管理
以Page为单位,底层用链表管理Page。Page按状态分三种:
- free page:空闲未使用。
- clean page:已使用,数据未修改。
- dirty page:已使用,数据修改,与磁盘数据不一致。
Change Buffer
定义
Change Buffer即更改缓冲区,针对非唯一二级索引。执行DML语句时,若相关数据页不在Buffer Pool,不直接操作磁盘,而是将数据变更存于Change Buffer,待数据被读取时,合并数据到Buffer Pool并刷新到磁盘。
意义
非唯一二级索引数据插入、删除和更新顺序相对随机,频繁操作磁盘会产生大量I/O。Change Buffer可在缓冲池中合并处理变更,减少磁盘I/O 。
Adaptive Hash Index
概述
Adaptive Hash Index即自适应hash索引,用于优化对Buffer Pool数据的查询。InnoDB存储引擎监控表索引页查询情况,若发现hash索引能提升查询速度,就自动建立,无需人工干预。
相关参数
adaptive_hash_index,可以通过show variables like "%adaptive_hash_index"来查看是否开启
Log Buffer
概述
Log Buffer即日志缓冲区,用于保存待写入磁盘的redo log、undo log等日志数据,默认大小16MB ,日志定期刷新到磁盘。对于涉及大量行更新、插入、删除的事务,增大其大小可节省磁盘I/O。
相关参数
- innodb_log_buffer_size:用于设置缓冲区大小。
- innodb_flush_log_at_trx_commit:控制日志刷新到磁盘的时机,取值及含义:
- 1:每次事务提交时写入并刷新到磁盘。
- 0:每秒将日志写入并刷新到磁盘一次。
- 2:每次事务提交后写入,并每秒刷新到磁盘一次。
磁盘架构
System Tablespace(系统表空间)
- 存储区域:是更改缓冲区的存储区域。若表在系统表空间创建,还可能包含表和索引数据。在MySQL 5.x版本中,还包含InnoDB数据字典、undo log等。
- 相关参数:innodb_data_file_path 。
File-Per-Table Tablespaces(独立表空间)
- 存储内容:每个表的文件表空间包含单个InnoDB表的数据和索引,存储在文件系统的单个数据文件中。
- 相关参数:innodb_file_per_table 。
General Tablespaces(通用表空间)
通过CREATE TABLESPACE语法创建,创建表时可指定使用该表空间,语法示例:
CREATE TABLESPACE table_space_name ADD DATAFILE 'file_name' ENGINE = engine_name;
CREATE TABLE xxx... TABLESPACE ts_name;
Undo Tablespaces(撤销表空间)
MySQL实例初始化时自动创建两个默认undo表空间(初始大小16M ),用于存储undo log日志。
Temporary Tablespaces(临时表空间)
InnoDB使用会话临时表空间和全局临时表空间,用于存储用户创建的临时表等数据。
Doublewrite Buffer Files(双写缓冲区)
InnoDB引擎将数据页从Buffer Pool刷新到磁盘前,先写入双写缓冲区文件(如#ib_16384_0.dblwr、#ib_16384_1.dblwr ),用于系统异常时恢复数据。
Redo Log(重做日志)
- 作用:实现事务持久性。
- 组成:由重做日志缓冲(内存中)和重做日志文件(磁盘中,如ib_logfile0、ib_logfile1 )组成。事务提交后,修改信息存入日志,用于脏页刷新出错时的数据恢复,以循环方式写入重做日志文件。
后台线程
后台线程用于同步内存缓冲区及磁盘区,主要有5种
Master Thread
核心后台线程,负责调度其他线程,异步将缓冲池数据刷新到磁盘,维持数据一致性,涵盖脏页刷新、合并插入缓存、undo页回收等工作。
IO Thread
InnoDB用AIO处理IO请求提升性能,IO Thread负责这些请求的回调。
| 线程类型 | 默认个数 | 职责 |
|---|---|---|
| Read thread | 4 | 负责读操作 |
| Write thread | 4 | 负责写操作 |
| Log thread | 1 | 将日志缓冲区刷新到磁盘 |
| Insert buffer thread | 1 | 将写缓冲区内容刷新到磁盘 |
Purge Thread
用于回收事务提交后不再使用的undo log 。
Page Cleaner Thread
协助Master Thread将脏页刷新到磁盘,减轻其工作压力,减少阻塞。
事务
概述
事务的特性有4个,分别是:
- 原子性
- 一致性
- 隔离性
- 持久性
其中原子性、一致性、持久性主要是通过redo log和undo log来实现的,而隔离性则是通过锁和mvcc来实现的
redo log
redo log概述
redo log即重做日志,记录事务提交时数据页的物理修改,用于实现事务的持久性。由重做日志缓冲(在内存)和重做日志文件(在磁盘 )组成。事务提交后,修改信息存入日志,用于脏页刷新出错时的数据恢复。
工作流程
在一个事务内执行update、delete等操作并提交事务后,相关数据页变化先存于Buffer Pool,同时写入Redolog buffer 。采用WAL(Write - Ahead Logging,预写日志)机制,优先记录日志到磁盘的重做日志文件(如ib_logfile0/1 ),而数据页不一定立刻刷新到磁盘(.ibd文件) ,保证事务提交后数据修改能持久化存储,若后续刷新脏页出错,可依据重做日志恢复数据。
性能
为什么不在事务提交的时候直接提交Buffer Pool中的数据到磁盘中?因为多条写入的数据一般都是随机io,性能比较低;而将日志写入磁盘则是添加新的日志,是顺序io,性能优于随机io。
undo log
功能
undo log即回滚日志,记录数据修改前信息,作用是提供回滚和支持MVCC(多版本并发控制),用于实现原子性 。
与redo log 区别
redo log是物理日志,记录数据页物理修改;undo log是逻辑日志 。比如delete记录时,undo log记一条insert记录;update时,记一条反向update记录,执行rollback时依此回滚。
生命周期与存储
- 销毁:事务执行产生undo log ,提交时不立即删除,因可能用于MVCC。
- 存储:采用段方式管理记录,存于rollback segment回滚段,回滚段含1024个undo log segment 。
MVCC
基本概念
当前读
读取记录最新版本,读取时对记录加锁,防止其他并发事务修改。像select ... lock in share mode(共享锁)、select ... for update、update、insert、delete(排他锁)操作都属于当前读 。
快照读
简单select(不加锁)操作,读取记录数据的可见版本,可能是历史数据,是非阻塞读。
不同隔离级别下特点:
- Read Committed:每次
select都生成一个快照读。 - Repeatable Read:开启事务后第一个
select语句触发快照读。 - Serializable:快照读退化为当前读。
MVCC(多版本并发控制)
全称Multi-Version Concurrency Control,维护数据多个版本,避免读写冲突。快照读为MySQL实现MVCC提供非阻塞读功能。
实现依赖数据库记录的三个隐式字段、undo log日志、readView 。
隐藏字段
在每张表创建时,mysql会为这张表自动创建2~3个隐藏字段
| 隐藏字段 | 含义 |
|---|---|
| DB_TRX_ID | 记录插入或最后一次修改该记录的事务ID 。 |
| DB_ROLL_PTR | 回滚指针,配合undo log,指向上一个版本 。 |
| DB_ROW_ID | 若表无指定主键,生成此隐藏主键。 |
undo log
undo log产生时机与用途
undo log即回滚日志,在insert、update、delete操作时产生,用于数据回滚。
不同操作下的生命周期
- insert操作:产生的undo log仅在回滚时需要,事务提交后可立即删除。
- update和delete操作:产生的undo log不仅回滚时需要,快照读时也需用到,不会在事务提交后立即删除。
undo log版本链
当不同事务或相同事务对同一条记录进行修改时,该记录的undo log会生成记录版本链表 。链表头部是最新的旧记录,尾部是最早的旧记录。 这一机制在实现MVCC(多版本并发控制)时发挥重要作用,可用于确定快照读时数据的可见版本等场景。
ReadView
ReadView定义
ReadView(读视图)是快照读SQL执行时MVCC(多版本并发控制)提取数据的依据,记录并维护系统当前活跃(未提交)的事务id 。
ReadView核心字段
| 字段 | 含义 |
|---|---|
| m_ids | 当前活跃的事务ID集合 |
| min_trx_id | 最小活跃事务ID |
| max_trx_id | 预分配事务ID,当前最大事务ID + 1(事务ID自增) |
| creator_trx_id | ReadView创建者的事务ID |
ReadView版本链数据访问规则
设trx_id为当前事务ID,依据以下规则判断能否访问版本链数据:
- 若
trx_id == creator_trx_id,可访问该版本,意味着数据由当前事务更改。 - 若
trx_id < min_trx_id,可访问该版本,表明数据已提交。 - 若
trx_id > max_trx_id,不可访问该版本,说明事务在ReadView生成后开启。 - 若
min_trx_id <= trx_id <= max_trx_id,且trx_id不在m_ids中,可访问该版本,说明数据已提交。
不同隔离级别下ReadView生成时机
- READ COMMITTED:事务中每次执行快照读都生成ReadView。
- REPEATABLE READ:事务中首次执行快照读生成ReadView,后续复用。