Server层
除了 数据存储与提取之外的所有事情都在这里完成
连接器Connector
- 核心职责:负责与客户端建立连接、获取权限、维持和管理连接。
- 工作机制:
- 校验客户端输入的用户名和密码。如果失败,返回错误提示。
- 认证通过后,连接器会去权限表里查出当前用户拥有的权限。此后这个连接里的权限判断,都将依赖此时读到的权限数据(这意味着用户建立连接后,管理员修改了该用户的权限,对已存在的连接不会生效,只有新建连接才会生效)。
- 负责管理空闲连接,超时未操作会自动断开(由
wait_timeout参数控制,默认 8 小时)。
查询缓存Query Cache
当执行查询时,会先看缓存里有没有相同的 SQL 语句和结果,如果有就直接返回,MySQL8.0版本后 已删除
- 被删除的原因:缓存的失效非常频繁。只要一个表有任何一条数据被更新,这个表对应的所有查询缓存都会被清空。对于写多读少的业务,查询缓存的命中率极低,还会带来额外的维护开销,得不偿失。
解析器Parser
- 核心职责:把客户端传过来的普通文本 SQL 语句,解析成 MySQL 能认识的内部数据结构。
- 工作机制:
- 词法分析:将 SQL 拆分成一个个独立的单词。识别出关键字(如
SELECT)、表名、列名等。 - 语法分析:根据语法规则,判断这个 SQL 语句是否满足 MySQL 的语法。如果不满足,就会抛出经典的异常:
You have an error in your SQL syntax。
- 词法分析:将 SQL 拆分成一个个独立的单词。识别出关键字(如
预处理器Preprocessor
- 核心职责:进一步检查解析树的合法性。
- 工作机制:
- 检查 SQL 语句中的表名、列名是否存在。
- 检查用户是否有操作该表或该列的权限。
优化器Optimizer
- 核心职责:决定 SQL 语句的执行方案。这是最影响性能的环节。
- 工作机制:
- 当一条 SQL 有多种执行路径时(例如表里有多个索引,或者多表关联查询),优化器会基于 成本(Cost) 评估,选择它认为效率最高的一种。
- 常见优化动作:决定使用哪个索引;决定多表连接(Join)时的各个表的加载顺序。
执行器Executor
- 核心职责:开始执行 SQL 语句,负责与存储引擎交互。
- 工作机制:
- 在执行前,先验证当前用户对该表有没有执行该语句的权限(如没有,直接报错)。
- 如果有权限,就根据优化器生成的执行计划,一步步调用存储引擎提供的 API 接口获取数据。
- 将存储引擎返回的数据收集起来,最终返回给客户端。
存储引擎层
只负责数据的存储和提取
-
插件式架构:MySQL 支持多种存储引擎,它们都实现了相同的 API 接口。Server 层不关心底层数据怎么存,只通过接口收发数据。
-
核心引擎:
- InnoDB:MySQL 5.5.5 版本开始的默认存储引擎。全面支持事务(ACID)、行级锁、外键以及崩溃恢复能力,是绝大多数业务的首选。
- MyISAM:早期的默认引擎。不支持事务,只支持表级锁,但读取速度快,占用资源少,目前极少在核心业务中使用。
- Memory:数据全部存放在内存中,读写速度极快,但断电后数据会全部丢失,常用于临时表。
| 对比维度 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持(具备 ACID 特性) | 不支持 |
| 锁粒度 | 支持行级锁、表级锁 | 仅支持表级锁 |
| 外键 | 支持 | 不支持 |
| 崩溃恢复 | 支持(依赖 redo log) | 不支持 |
| 主键 | 必须有主键,默认自增 | 可以没有主键 |
| 核心应用场景 | 绝大多数业务、高并发、需要事务的场景 | 读多写少、不需要事务、对原子性无要求的场景 |
InnoDB
完美支持 ACID 事务特性、提供了行级锁,并具备高可靠的崩溃恢复(Crash-safe)能力
内存结构
由于磁盘 I/O 性能较低,InnoDB 绝大部分数据的读写和修改都在内存中进行。
缓冲池 (Buffer Pool)
Buffer Pool 是 InnoDB 最核心的内存区域(通常建议占用服务器 60%~80% 的内存)。
-
页(Page):InnoDB 与磁盘交互的最小单位是“页”,默认 16KB。Buffer Pool 缓存了大量的索引页数据和数据页。
-
读写加速:
- 读数据:优先看 Buffer Pool 里有没有,有就直接返回(命中缓存);没有就从磁盘加载到 Buffer Pool 再返回。
- 写数据:直接修改 Buffer Pool 中的页。被修改后与磁盘不一致的页称为 “脏页”(Dirty Page)。脏页由后台线程异步刷入磁盘(CheckPoint 机制)。
-
内存管理(LRU 链表):InnoDB 对经典 LRU 算法进行了改进(分为 New Sublist 和 Old Sublist),防止全表扫描时把热点数据挤出内存。
写缓冲区 (Change Buffer)
- 作用:针对 非唯一二级索引(Secondary Index) 的写优化。
- 原理:当执行
INSERT或UPDATE时,如果对应的二级索引页不在 Buffer Pool 中,InnoDB 不会立刻从磁盘读入该页,而是将修改记录在 Change Buffer 中。当后续该页被读取时,再将修改 Merge(合并) 进去。这避免了大量随机磁盘 I/O。
日志缓冲区 (Log Buffer)
- 作用:用来缓存即将写入 Redo Log 的内存区域,默认 16MB。
- 策略:事务提交或每隔 1 秒,Log Buffer 中的内容会刷入磁盘的 Redo Log 文件中(由
innodb_flush_log_at_trx_commit参数控制)。
磁盘结构
表空间 (Tablespaces)
- 系统表空间 (System Tablespace):包含 InnoDB 数据字典、双写缓冲区等。默认对应磁盘上的
ibdata1文件。 - 独立表空间 (File-Per-Table Tablespace):开启
innodb_file_per_table(默认开启)后,每张表会拥有一个独立的表名.ibd文件,存放该表的数据、索引和插入缓冲。
双写缓冲区 (Doublewrite Buffer)
- 解决痛点——页断裂(Partial Page Write):Linux 文件系统的一页通常是 4KB,而 InnoDB 一页是 16KB。当数据库正在往磁盘写 16KB 的页时,如果服务器突然断电,可能只写了 4KB,导致数据页损坏且无法通过 Redo Log 修复。
- 原理:脏页在刷盘前,会先顺序写到磁盘的 Doublewrite Buffer 副本中,然后再写入实际的
.ibd数据文件。如果写.ibd时崩溃,可以通过 Doublewrite Buffer 找到完整页进行恢复。
事务崩溃恢复原理
Redo Log(重做日志)
保证持久性(Durability)
- WAL 技术(Write-Ahead Logging,日志先行):在修改页之前,必须先把修改行为记录到 Redo Log 中并刷盘,才算事务提交成功。
- 顺序 IO vs 随机 IO:修改数据页是磁盘随机 I/O(极慢),而追加 Redo Log 是磁盘顺序 I/O(极快)。
- 崩溃恢复:如果系统断电,Buffer Pool 中的脏页丢失了,重启时 InnoDB 会读取磁盘上的 Redo Log,把没有来得及刷盘的改动“重做”一遍。
Undo Log(回滚日志)
保证原子性(Atomicity)与 MVCC
- 事务回滚:记录的是逻辑反向日志。当你
INSERT一条记录,Undo Log 就会记录一条对应的DELETE;当你UPDATE,它记录修改前的值。如果事务失败或执行了ROLLBACK,就执行反向操作。 - MVCC(多版本并发控制):Undo Log 会形成一个 “版本链”。当 A 事务正在修改某行,B 事务来读取时,B 事务可以通过 Undo Log 链条读取到修改前的历史版本(快照读),从而实现读写不冲突。
后台线程
InnoDB 内部有一组常驻的后台线程,负责调度和清理工作
- Master Thread:核心主线程,负责异步将缓冲池中的数据刷到磁盘,包括脏页刷新、合并 Change Buffer 等。
- IO Thread:负责处理 AIO(异步I/O)请求。包含 Read Thread、Write Thread、Log Thread。
- Purge Thread:事务提交后,其使用的 Undo Log 就没有用了。Purge 线程负责回收这些已经无用的 Undo Log 页面。
- Page Cleaner Thread:专门负责脏页的刷新工作,减轻 Master Thread 的压力,提高并发性能