目录标题
- 一、MySQL的体系结构
- 二、存储引擎
- 三、InnoDB
-
- 1.逻辑存储结构
- 2.内存结构
- 3.磁盘结构
- 4.后台线程
- 5.事务的原理
-
- [(1)redo log(持久性保证)](#(1)redo log(持久性保证))
- [(2)undo log(原子性)](#(2)undo log(原子性))
- (3)MVCC(多版本并发控制)
-
- a.三个隐式字段
- [b.undo log 版本链](#b.undo log 版本链)
- c.ReadView(读视图)
- 四、MyISAM
- 五、Memory
- 六、InnoDB和MyISAM的区别
- 七、存储引擎的选择
一、MySQL的体系结构
MySQL的体系结构主要分为以下四个层次:
- 连接层:负责客户端连接管理、授权认证、安全方案等。每个客户端连接在服务端会分配一个线程。
- 服务层:提供SQL解析、查询优化、缓存、内置函数(如日期、时间、数学函数)以及跨存储引擎的功能(如存储过程、触发器、视图等)。
- 存储引擎层:负责数据的存储和提取。MySQL支持插件式存储引擎,不同的存储引擎决定了数据操作的特性(如是否支持事务、行锁等)。
- 文件系统层:将数据库数据存储在磁盘上(如数据文件、日志文件等)。

二、存储引擎
在创建表时可以通过 ENGINE 关键字指定存储引擎,如下例所示,如果没有指定,MySQL会使用默认存储引擎(MySQL 5.5 之后默认为 InnoDB)。
sql
CREATE TABLE mytable (
id INT PRIMARY KEY,
name VARCHAR(50)
) ENGINE = InnoDB;
当前数据库支持的常用的存储引擎
- InnoDB:支持事务、行级锁、外键,高并发性能,默认存储引擎
- MyISAM 不支持事务,表级锁,访问速度快,早期默认引擎
- Memory 基于内存存储,数据断电丢失,表级锁,常用于临时表
三、InnoDB
InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后成为默认的 MySQL 存储引擎。
其特点如下,
- DML 操作遵循 ACID 模型,
支持事务(Transaction)。 行级锁(Row-Level Locking),提高并发访问性能。- 支持
外键(FOREIGN KEY)约束,保证数据的完整性和正确性。
每个 InnoDB 表对应一个 .ibd 文件(表空间文件),例如 表名.ibd。该文件存储了该表的表结构(.frm / .sdi)、数据和索引。相关参数:innodb_file_per_table(默认开启,表示每张表独立表空间)。
1.逻辑存储结构
从大到小依次为:表空间 → 段 → 区 → 页 → 行。
- 表空间(Tablespace):一个 MySQL 实例可以对应多个表空间(如系统表空间、独立表空间、通用表空间)。用于存储记录、索引等数据。
- 段(Segment):分为数据段(Leaf node segment,对应 B+Tree 叶子节点)、索引段(Non-leaf node segment,对应 B+Tree 非叶子节点)、回滚段(Rollback segment)。段用来管理多个区(Extent)。
- 区(Extent):表空间的单元结构,每个区大小为 1 MB。默认情况下,InnoDB 页大小为 16 KB,所以一个区包含 64 个连续页。
- 页(Page):InnoDB 磁盘管理的最小单元,每个页默认 16 KB。为保证连续性,InnoDB 每次从磁盘申请 4~5 个区。
- 行(Row):InnoDB 按行存储数据。

2.内存结构

- Buffer Pool(缓冲池) :缓冲池是主内存中的一个区域,用来缓存磁盘上经常操作的真实数据。在执行增删改查时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后以一定频率刷新到磁盘。这样可以减少磁盘 I/O,加快处理速度。
- 缓冲池以 页(Page) 为单位,底层采用链表数据结构管理 Page。根据状态,Page 分为三种类型:
- free page:空闲页,未被使用。
- clean page:已使用页,数据没有被修改过(与磁盘一致)。
- dirty page:脏页,数据被修改过,与磁盘不一致。
- Adaptive Hash Index(自适应哈希索引):用于优化对 Buffer Pool 中数据的查询。InnoDB 会监控对表上索引页的查询,如果观察到哈希索引可以提升速度,则自动建立哈希索引,无需人工干预。相关参数:adaptive_hash_index。
- Change Buffer(更改缓冲区) :当执行 INSERT、UPDATE、DELETE 等 DML 语句时,如果目标数据页(Page)不在 Buffer Pool 中,InnoDB 不会直接读取磁盘页进行修改,而是将变更操作缓存在 Change Buffer 中。未来当该数据页被读取(例如通过查询或索引扫描)时,InnoDB 会将 Change Buffer 中缓存的变更合并(Merge) 回 Buffer Pool 中的相应页,然后再将合并后的数据刷新到磁盘。
- Change Buffer 只对 非唯一二级索引 生效。唯一索引和主键索引需要立即检查唯一性约束,因此不能缓存,必须直接读取磁盘页。
- 二级索引(尤其是非唯一二级索引)的插入、删除、更新往往涉及随机的磁盘位置。如果没有 Change Buffer,每次 DML 都需要直接读写磁盘页,造成大量随机 I/O,性能极低。
- Log Buffer(日志缓冲区) :用来保存要写入磁盘的日志数据(redo log、undo log)。默认大小为 16 MB。日志缓冲区的日志会定期刷新到磁盘。如果需要更新、插入或删除许多行的事务,增加日志缓冲区的大小可以节省磁盘 I/O。相关参数如下
-
innodb_log_buffer_size:缓冲区大小。
-
innodb_flush_log_at_trx_commit:日志刷新到磁盘的时机。
- 1:每次事务提交时写入并刷新到磁盘(最安全)。
- 0:每秒将日志写入并刷新到磁盘一次。
- 2:每次事务提交后写入,每秒刷新到磁盘一次。
-
3.磁盘结构

- System Tablespace(系统表空间):系统表空间是更改缓冲区的存储区域。如果表是在系统表空间(而不是每个表文件或通用表空间)中创建的,它也可能包含表和索引数据。在 MySQL 5.x 版本中还包含 InnoDB 数据字典、undo log 等。相关参数:innodb_data_file_path。
- File-Per-Table Tablespaces(独立表空间):每个表的文件表空间包含该表的数据和索引,存储在文件系统上的单个数据文件(表名.ibd)中。相关参数:innodb_file_per_table(默认 ON)。
- General Tablespaces(通用表空间):需要通过 CREATE TABLESPACE 语法创建通用表空间,在创建表时可以指定使用该表空间。
sql
CREATE TABLESPACE xxxx ADD
DATAFILE 'file_name'
ENGINE = engine_name;
- Undo Tablespaces(撤销表空间):MySQL 实例在初始化时会自动创建两个默认的 undo 表空间(初始大小 16 MB),用于存储 undo log 日志。
- Temporary Tablespaces(临时表空间):InnoDB 使用会话临时表空间和全局临时表空间,存储用户创建的临时表等数据。
- Doublewrite Buffer Files(双写缓冲区):InnoDB 引擎将数据页从 Buffer Pool 刷新到磁盘之前,先将数据页写入双写缓冲区文件中,以便系统异常时恢复数据。相关文件为 #ib_16384_0.dblwr、#ib_16384_1.dblwr。
- Redo Log(重做日志):重做日志用于实现事务的持久性。由两部分组成:重做日志缓冲(redo log buffer)(内存)和重做日志文件(redo log file)(磁盘)。事务提交后,所有修改信息都会写入该日志,用于在刷新脏页到磁盘发生错误时进行数据恢复。重做日志以循环方式写入,默认涉及两个文件:ib_logfile0、ib_logfile1。
4.后台线程
| 线程类型 | 默认个数 | 职责 |
|---|---|---|
| Master Thread | 1 | 核心后台线程,负责调度其他线程,将缓冲池中的数据异步刷新到磁盘,保持数据一致性(包括脏页刷新、合并插入缓存、undo 页回收)。 |
| IO Thread | 多个 | 负责处理异步 I/O(AIO)请求的回调。具体包括: • Read thread(4个):负责读操作 • Write thread(4个):负责写操作 • Log thread(1个):负责将日志缓冲区刷新到磁盘 • Insert buffer thread(1个):负责将写缓冲区内容刷新到磁盘 |
| Purge Thread | 1 | 回收事务已提交的 undo log。 |
| Page Cleaner Thread | 1 | 协助 Master Thread 刷新脏页到磁盘,减轻 Master Thread 压力,减少阻塞。 |
5.事务的原理
- 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚,通过undo log保证
- 一致性(Consistency):事务执行前后,数据完整性约束没有被破坏,通过undo log + redo log + 其他机制保证
- 隔离性(Isolation):多个事务并发执行时,相互隔离,互不干扰,通过锁机制 + MVCC保证
- 持久性(Durability):事务一旦提交,其结果就是永久性的,通过redo log实现
原子性、一致性、持久性:undo log和redo log 保证
隔离性:事务和MVCC机制
(1)redo log(持久性保证)
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
如下例所示,事务提交后,数据页(修改后的脏页)还在 Buffer Pool 中,并不会立即写入磁盘;后台线程负责刷新脏页,但此时如果刷新失败,则会导致数据不一致的问题。
如果直接刷新数据页到磁盘,此时的刷新操作是随机磁盘 I/O(这里的随机就是指更改的操作随机发生在缓冲区中,即一会update a,一会update b),这样性能低,但如果不即使刷新就可能丢失缓冲区中的数据,导致前后不一致。
采用redo log日志的方式,update操作缓冲区会同步将这个更新操作写入redo log buffer中(记录数据页的变化),随后将数据持久化保存到磁盘文件当中 ;如果后面脏页刷新的时候失败,可以通过redo log进行数据恢复 。(日志文件都是追加的,即顺序IO,效率较高),此过程成为WAL(Write-Ahead Logging),即日志先行。隔一段时间后会进行log文件清理。

(2)undo log(原子性)
回滚日志用于记录数据被修改前的信息,作用包括:提供回滚功能和支持 MVCC(多版本并发控制)。
undo log 和 redo log记录物理日志不一样,它是逻辑日志。例如,执行 DELETE 时,undo log 记录一条对应的 INSERT;执行 UPDATE 时,记录一条相反的 UPDATE。
- 事务回滚:当执行rollback日志回滚时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。
- Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志可能还用于MVCC。
- Undo log存储:undo log采用段的方式进行管理和记录,存放在前面介绍的 rollback segment 回滚段中,内部包含1024个undo log segment。
(3)MVCC(多版本并发控制)
再进行MVCC多版本并发控制的时候,涉及到以下几个概念:
- 当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。
sql
--服务器1
begin;
select 查询到旧数据
--服务器2进行数据更新操作
select 还是旧数据(因为默认的隔离级别是可重复读)
--当前读,读取的是新数据
select ... lock in share mode 新数据
- 快照读 :简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
- Read Committed 隔离级别:每次select,都生成一个快照读。
- Repeatable Read 隔离级别:开启事务后第一个select语句才是快照读的地方。
- Serializable 隔离级别:快照读会退化为当前读。
sql
--默认的隔离级别
--服务器1
begin;
select 查询到旧数据同时生成快照
--服务器2进行数据更新操作,事务提交
select 还是旧数据(读取的是第一个快照)
- MVCC:即 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现 MVCC 提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView。
当前读并不依赖多版本,而是依赖锁来实现隔离
快照读 是 MVCC 机制的直接体现
a.三个隐式字段
数据库表中每行记录除了用户定义的列外,还包含以下隐藏字段:
- DB_TRX_ID:最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
- DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
- DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。
| id | age | name | DB_TRX_ID | DB_ROLL_PTR | DB_ROW_ID |
|---|---|---|---|---|---|
| 1 | 1 | tom | 100 | pointer1 | (自动生成) |
| 3 | 3 | cat | 101 | pointer2 | (自动生成) |
b.undo log 版本链
undo log 通过 DB_ROLL_PTR 将不同版本的记录串联起来,形成版本链。每次更新都会产生一条新的 undo log 记录,指向旧版本。

INSERT 产生的 undo log 只在回滚时需要,事务提交后可立即删除。
UPDATE、DELETE 产生的 undo log 在回滚和快照读时都需要,不会立即删除。
c.ReadView(读视图)
ReadView 是快照读执行时 MVCC 提取数据的依据,记录并维护系统当前活跃的事务(未提交)ID。核心字段如下所示:
| 字段 | 含义 |
|---|---|
| m_ids | 当前活跃的事务 ID 集合 |
| min_trx_id | 最小活跃事务 ID |
| max_trx_id | 预分配事务 ID(当前最大事务 ID + 1,因为事务 ID 自增) |
| creator_trx_id | 创建该 ReadView 的事务 ID |
可见性判断规则:当读取一条记录时,拿到该记录的 DB_TRX_ID(记为 trx_id),判断如下:
- trx_id == creator_trx_id → 可以访问(当前事务自己的修改)。
- trx_id < min_trx_id → 可以访问(该版本在 ReadView 创建前已提交)。
- 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,后续复用。
如下图所示,在RC隔离级别下,事务5中的查询id=30记录会生成一个ReadView,当前活跃的事务即还没提交的事务m_ids={3,4,5},min_trx_id=3,max_trx_id=5+1,创建该 ReadView 的事务 ID creator_trx_id=5。如上例所示,当前记录的DB_TRX_ID=4(即最近对这个数据进行操作的是事务4)。按照上面的规则4 != 5,4>3,4<6,3<=4<=6但是4存在m_ids中,都不满足,所以不可以访问,所以这一次快照读不应该读当前的数据,这个时候就会通过指针找上一条记录,再进行对比,直到满足可以访问的条件(trx_id的时候才满足可以访问,可以理解为查询的时候只有事务2提交了,所以可以查询到事务2 更改后的结果)

四、MyISAM
MyISAM 是 MySQL 早期的默认存储引擎(MySQL 5.5 之前)。其特点如下,
- 不支持事务,不支持外键。
- 支持表锁,不支持行锁。
- 访问速度快,适合读密集型、写少的场景。
每个 MyISAM 表对应三个文件:
- xxx.sdi(或 .frm):存储表结构信息。
- xxx.MYD:存储数据。
- xxx.MYI:存储索引。
五、Memory
Memory 引擎将数据存储在内存中,访问速度极快,但服务器重启后数据丢失。其特点如下,
- 基于哈希索引,默认支持 B+Tree 索引。
- 表级锁,不支持事务。
- 常用于临时表或缓存表。
每个 Memory 表只对应一个 .sdi 文件(表结构),数据不持久化到磁盘。
六、InnoDB和MyISAM的区别
| 对比项 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持 | 不支持 |
| 外键支持 | 支持 | 不支持 |
| 锁粒度 | 行锁 + 表锁 | 表锁 |
| 并发性能 | 高(行锁) | 低(表锁) |
| 数据恢复 | 通过 redo log 自动恢复 | 需要手动修复 |
| MVCC 支持 | 支持 | 不支持 |
| 全文索引 | 支持(MySQL 5.6+) | 支持 |
| 文件格式 | .ibd(数据和索引一起) | .MYD(数据)+ .MYI(索引) |
| 适用场景 | OLTP(高并发写、事务) | OLAP(读多写少,如数据仓库) |
七、存储引擎的选择
| 场景 | 推荐存储引擎 | 原因 |
|---|---|---|
| 高并发、事务型业务(如银行、电商订单) | InnoDB | 支持事务、行锁、高并发 |
| 读多写少、对事务无要求(如日志、报表) | MyISAM | 读速度快,占用空间小 |
| 临时缓存、会话数据(数据可丢失) | Memory | 内存速度极快 |
| 需要全文搜索且对事务不敏感 | MyISAM 或 InnoDB 5.6+ | 两者都支持全文索引,但 InnoDB 更全面 |