MySQL 核心架构与底层原理总结

8 阅读8分钟

MySQL 核心架构与底层原理总结

1. MySQL 体系架构与核心机制

在深入研究 MySQL 的日志和内存机制之前,我们需要先理解 MySQL 的分层架构以及它如何保证数据库最核心的特性——ACID。

1.1 MySQL 逻辑架构

MySQL 的架构可以清晰地分为两层:Server 层 和 ​存储引擎层。这种分层设计决定了不同日志(Binlog vs Redo Log)所处的位置和作用范围。

  • Server 层(通用层)

    • 包含连接器、查询缓存(8.0已移除)、分析器、优化器、执行器等。
    • 核心职责:负责 SQL 语法解析、优化、以及与客户端的交互。
    • 对应日志​:Binlog (归档日志) 就在这一层产生。因此 Binlog 是所有存储引擎共有的,不涉及底层数据页的物理细节。
  • 存储引擎层(插件式)

    • 负责数据的存储和提取。最常用的是 ​InnoDB
    • 核心职责:负责事务管理、锁机制、以及数据的持久化。
    • 对应日志​:Redo Log (重做日志)Undo Log (回滚日志) 是 InnoDB 引擎特有的,它们深入到物理“数据页”的级别。

1.2 ACID 特性与实现的对应关系

我们学习所有的日志(Redo/Undo)和机制(MVCC/锁),最终目的都是为了实现事务的 ACID 特性。面试时,将组件与 ACID 对应起来是满分回答:

  • A (Atomicity) 原子性:要么全做,要么全不做。

    • 实现依靠​:​Undo Log。如果事务失败,靠 Undo Log 回滚。
  • C (Consistency) 一致性:数据库从一个一致性状态变到另一个一致性状态。

    • 实现依靠:这是最终目的,依靠 A、I、D 三者共同保证,以及代码层面的约束。
  • I (Isolation) 隔离性:多个事务并发执行不互相干扰。

    • 实现依靠​:锁 (Locks) 和 ​MVCC (多版本并发控制)
  • D (Durability) 持久性:事务一旦提交,数据永不丢失。

    • 实现依靠​:Redo Log 和 ​Double Write Buffer (双写缓冲)

1.3 核心设计思想:WAL (Write-Ahead Logging)

MySQL 能够支持高性能写入的关键技术是 ​WAL(预写日志)

  • 问题:如果每次修改数据都直接去写硬盘上的数据文件,由于是随机 IO,速度会极慢。

  • 解决:先写日志(Redo Log),再更新内存(Buffer Pool)。

  • 机制

    1. 当有记录需要更新时,InnoDB 会先把记录写到 Redo Log(这是顺序 IO,速度快)。
    2. 同时更新内存(Buffer Pool)。
    3. 此时事务就算完成了。
    4. 至于什么时候真正把数据写到磁盘的数据文件里,由后台线程慢慢做(这个过程叫 ​Checkpoint)。

1.4 数据存储结构与索引组织 (B+ Tree)

有了架构和日志的概念后,我们需要知道数据到底是怎么存的。在 InnoDB 中,​数据即索引,索引即数据

  • 页 (Page) —— 最小的物理单位​:InnoDB 不会按“行”读取数据,那样太慢。它和磁盘交互的最小单位是 ​Page(默认 16KB)。

  • 索引结构 (B+ Tree)

    • 高度低:B+ 树非叶子节点只存索引(key),不存数据,能容纳更多索引项。一张千万级的表,树高通常只有 3 层,意味着只需 3 次磁盘 IO。
    • 范围查询强​:B+ 树叶子节点之间有双向指针连接,非常适合 > 100 这种范围扫描。
  • 聚簇索引 (Clustered Index)

    • 即主键索引。​特点​:叶子节点存的是​完整的整行数据。InnoDB 的数据文件(.ibd)本身就是一棵聚簇索引树。
  • 非聚簇索引 (Secondary Index)

    • 即普通索引。​特点​:叶子节点存的是 ​索引列的值 + 主键 ID​。查询时如果需要非索引列,需要拿着 ID 回到聚簇索引再查一遍,称为​回表

2. MySQL 的主要日志

Binlog 主要用来对数据库进行备份、恢复和复制。Redo Log 和 Undo Log 主要用于事务,记录的是数据修改操作和回滚操作。

2.1 Binlog (归档日志)

Binlog 是 MySQL Server 层记录的所有 DDL 和 DML 语句的二进制日志。

  • 核心作用​:主从复制 (Replication) 和 ​数据恢复 (Point-in-Time Recovery)

  • 物理结构:追加写(Append-only),逻辑日志。

  • 格式

    • STATEMENT:记 SQL 语句(可能导致主从不一致)。
    • ROW:记每一行的具体变更(最安全,但空间占用大)。
    • MIXED:混合模式。
  • 刷盘策略​:由 sync_binlog 控制。

    • 0:交给 OS,高性能但风险大。
    • 1:每次提交都 fsync,最安全。

2.2 Redo Log (重做日志)

Redo Log 是 InnoDB 用于实现崩溃恢复(Crash Safe)的机制。

  • 核心作用​:保证事务的 ​持久性 (Durability)

  • 物理结构:物理日志(记录“某页某偏移量修改了什么”),循环写入(Ring Buffer)。

  • 刷盘策略​:由 innodb_flush_log_at_trx_commit 控制。

    • 1(默认):每次提交都写入磁盘,最安全。

2.3 Undo Log (回滚日志)

Undo Log 用于事务回滚和 MVCC。

  • 核心作用​:保证事务的 原子性 (Atomicity) 和 ​隔离性 (MVCC)
  • 物理结构:逻辑日志(记录相反操作)。
  • 存储位置:Undo Log 本身被当做“数据”对待,存放在 Buffer Pool 中,也会生成对应的 Redo Log。

2.4 三大日志总结对比

特性Redo Log (重做日志)Binlog (归档日志)Undo Log (回滚日志)
层级InnoDB 存储引擎层MySQL Server 层InnoDB 存储引擎层
内容物理日志 (Page 的修改)逻辑日志 (SQL/行变化)逻辑日志 (反向操作)
写入方式循环写 (环形,覆盖旧值)追加写 (不覆盖)随机读写 (复用空间)
缓冲区innodb_log_bufferbinlog_cache(Per Thread)Buffer Pool(作为数据页)
刷盘参数innodb_flush_log_at_trx_commitsync_binlog随 Checkpoint 机制
关键风险Write Pos 追上 Checkpoint (抖动)没刷盘导致主从不一致长事务导致空间膨胀
主要用途崩溃恢复 (Crash Safe)主从复制、数据恢复事务回滚、MVCC

2.5 Redo Log 与 Undo Log 比较

  • 目的:Redo Log 负责“前滚”(恢复数据),Undo Log 负责“回滚”(撤销操作)。
  • 记录内容:Redo Log 记物理修改,Undo Log 记逻辑反向操作。

2.6 MVCC (多版本并发控制) 详解

MVCC 是实现 RC (读已提交) 和 RR (可重复读) 隔离级别的核心技术。核心思想是:​读不加锁,读写不冲突

  1. 隐藏字段​:每行数据都有 trx_id​ (最近修改事务ID) 和 roll_pointer (回滚指针)。

  2. Undo Log 版本链:旧数据通过指针串联,形成历史版本链。

  3. Read View (一致性视图) ​:快照读时生成的“裁判”。包含 m_ids​ (活跃事务列表)、min_trx_id​、max_trx_id

    • 可见性规则​:通过比较数据的 trx_id 和 Read View 中的 ID,判断当前事务能看到哪个版本的数据。
    • RC vs RR:RC 每次 Select 都生成新的 Read View;RR 只在第一次 Select 生成,之后复用。

3. 两阶段提交 (Two-Phase Commit)

MySQL 使用两阶段提交来保证 Redo Log 和 Binlog 的逻辑一致性。

3.1 流程演示 (Update 语句)

假设执行 UPDATE t_user SET age = 18 WHERE id = 1; (双1模式):

  1. 执行阶段

    • 写入 Undo Log (内存)。
    • 更新 Buffer Pool 中的数据页 (内存变脏)。
    • 写入 Redo Log Buffer (内存)。
  2. 提交阶段 (2PC)

    • Prepare 阶段​:Redo Log 落盘 (fsync),标记为 PREPARE
    • Binlog 阶段:Binlog 落盘 (fsync)。
    • Commit 阶段​:Redo Log 标记为 COMMIT (通常只需内存操作,依赖 Binlog 存在性)。

3.2 崩溃恢复逻辑

  • 如果 Redo Prepare + Binlog 缺失 -> ​回滚
  • 如果 Redo Prepare + Binlog 完整 -> ​提交

4. InnoDB Buffer Pool (内存架构)

InnoDB 的核心内存组件,用于缓解 CPU 与磁盘的速度差异。

4.1 核心作用

  • 读缓存:命中内存则不读盘。
  • 写缓存:修改只改内存(脏页),通过 Checkpoint 异步刷盘。

4.2 内存管理 (三大链表)

  1. Free List:空闲页链表。
  2. Flush List:脏页链表(按修改时间排序,用于 Checkpoint)。
  3. LRU List:管理冷热数据淘汰。

4.3 改进版 LRU 算法

为了防止 全表扫描污染 Buffer Pool,InnoDB 将 LRU 分为 Young 区 (热, 63%) 和 ​Old 区 (冷, 37%)

  • 新读入的页先放 Old 区头部。
  • 只有在 Old 区停留超过 1 秒再次被访问,才移入 Young 区。

4.4 配置建议

  • innodb_buffer_pool_size:建议设为物理内存的 70%-80% (专用服务器)。
  • innodb_buffer_pool_instances​:内存 > 1G 时建议调大,减少锁竞争。
  • 预热​:开启 dump_at_shutdown​ 和 load_at_startup

5. Checkpoint 与 持久化机制

协调内存脏页与磁盘文件的同步,释放 Redo Log 空间。

5.1 核心概念:LSN

  • Redo LSN (写到哪) vs Checkpoint LSN (刷到哪)。
  • 差值即为“未落盘的脏数据量”。

5.2 触发场景

  • Master Thread:定时异步刷。
  • Redo Log 空间不足 (Async/Sync Flush) :最危险的情况,会导致性能抖动。
  • 内存不足:Free List 空了,被迫刷脏页腾地。

5.3 Double Write Buffer (双写缓冲) —— 最后一道防线

Q: 有了 Redo Log 为什么还要 Double Write?

A: 解决 页断裂 (Partial Page Write) 问题。

  • 问题:如果 16KB 的页只写了 4KB 就断电,物理页损坏,Redo Log 无法恢复。

  • 流程

    1. 脏页先 memcpy 到内存的双写缓冲区。
    2. 顺序写 入系统表空间 (ibdata1) 的双写区域 (备份)。
    3. 离散写 入真正的数据文件 (.ibd)。
  • 恢复:启动时若发现页损坏,先从 ibdata1 里的备份还原,再重放 Redo Log。