Skip to content

MySQL 从零到精通验收清单

这页专门回答一个问题:这些 MySQL 文档到底能不能让零基础学到能做项目、能讲原理、能过面试、能排查线上问题?

只说“能”没有意义,必须有可验证标准。你学完 MySQL 专栏后,至少要能独立完成下面这些任务:

  1. 根据业务需求设计表、字段、约束和索引。
  2. 写出增删改查、分页、Join、聚合、事务 SQL。
  3. 解释一条 select 从客户端到 InnoDB 读页的全过程。
  4. 解释一条 update 从加锁、undo、Buffer Pool、redo、binlog 到提交的全过程。
  5. 看懂 EXPLAIN,知道 typekeyrowsfilteredExtra 从哪里来。
  6. 解释 B+Tree、聚簇索引、二级索引、回表、覆盖索引、最左前缀。
  7. 解释事务、锁、MVCC、Read View、undo log 的关系。
  8. 解释 redo log、binlog、两阶段提交、主从复制和崩溃恢复。
  9. 排查慢 SQL、锁等待、死锁、主从延迟、大表深分页、覆盖索引仍慢。
  10. 面试时能先给标准回答,再跳到原理页讲清为什么。

如果只能背“索引用 B+Tree、事务 ACID、MVCC 多版本”,但不能解释为什么、不这样会怎样、线上怎么排查,就还没有达到目标。

总路线

mermaid
flowchart TD
    A["阶段 1<br/>SQL 和表设计"] --> B["阶段 2<br/>索引和 B+Tree"]
    B --> C["阶段 3<br/>执行计划和优化器"]
    C --> D["阶段 4<br/>事务、锁、MVCC"]
    D --> E["阶段 5<br/>undo、redo、binlog"]
    E --> F["阶段 6<br/>主从、备份、大表治理"]
    F --> G["阶段 7<br/>生产排查"]
    G --> H["阶段 8<br/>面试表达"]

为什么必须按这个顺序?

顺序原因如果跳过
先 SQL数据库最终执行的是 SQL后面看执行计划没有对象
再表设计表结构决定索引、事务和扩展性字段混乱,后续优化很难
再索引索引决定扫描范围和排序方式慢 SQL 只能乱加索引
再执行计划证明数据库到底怎么查无法解释有索引为什么不用
再事务并发商业系统最怕数据错支付、库存、状态流转容易错
再日志恢复提交、回滚、复制、恢复都靠日志宕机和主从不一致讲不清
最后排查生产问题通常是多机制叠加只会重启或猜原因

阶段 1:SQL 和表设计

必须掌握

知识点必须会什么原理页
字段类型bigintvarchardecimaldatetimetinyint 的选择SQL 基础
主键为什么 InnoDB 推荐递增主键存储结构
唯一约束为什么数据库层必须兜底唯一性表设计
非空和默认值为什么不要把数据正确性全交给 Java表设计
状态字段为什么状态要可控,不能随便字符串从零到生产级掌握
时间字段为什么 created_atupdated_at 对排查和归档重要表设计

商业 Demo:订单和支付表

sql
create table payment_order (
  id bigint primary key auto_increment,
  order_no varchar(64) not null,
  user_id bigint not null,
  pay_channel varchar(32) not null,
  status tinyint not null,
  amount decimal(12, 2) not null,
  paid_at datetime null,
  created_at datetime not null default current_timestamp,
  updated_at datetime not null default current_timestamp on update current_timestamp,
  unique key uk_order_no (order_no),
  key idx_user_status_created (user_id, status, created_at)
) engine = InnoDB default charset = utf8mb4;

为什么这样设计:

设计为什么不这样会怎样
id bigint auto_increment主键递增,聚簇索引插入更友好随机主键容易页分裂,二级索引也更大
order_no unique防止重复支付单并发请求可能写入重复业务单
amount decimal金额不能用浮点double 可能出现精度误差
status tinyint状态机可控且索引小状态乱写,统计和流转不可控
idx_user_status_created支撑用户订单列表用户订单页可能扫描大量数据

验收任务

你应该能独立完成:

  1. 设计一张 inventory_stock 库存表,包含商品 ID、可用库存、冻结库存、版本号、更新时间。
  2. 给订单号、资产编号、外部流水号设计唯一约束。
  3. 解释为什么金额不用 double
  4. 解释为什么数据库约束不能只靠 Java 校验替代。

阶段 2:索引和 B+Tree

索引到底是什么

索引不是“加速按钮”,而是数据库额外维护的一份有序结构。它能让查询少扫数据,但会增加写入成本、空间成本和维护成本。

mermaid
flowchart TD
    A["业务查询<br/>where user_id and status"] --> B{"有没有匹配索引"}
    B -- "没有" --> C["扫描大量数据页"]
    B -- "有" --> D["从 B+Tree 根页向下定位"]
    D --> E["找到叶子页范围"]
    E --> F["顺序扫描少量索引项"]
    F --> G{"返回列是否都在索引里"}
    G -- "是" --> H["覆盖索引返回"]
    G -- "否" --> I["回聚簇索引取整行"]

必须掌握

知识点必须会什么原理页
B+Tree为什么树高低、页分叉多、叶子有序B+Tree 与索引家族
聚簇索引为什么主键叶子节点保存整行存储结构
二级索引为什么叶子节点保存主键值索引
回表为什么查非索引列要再走主键树索引
覆盖索引为什么能减少回表,但不是万能大表覆盖索引仍然慢
联合索引为什么有最左前缀和列顺序索引
索引失效为什么函数、隐式转换、前导模糊可能导致扫更多优化相关

Demo:联合索引顺序

sql
create index idx_user_status_created
on payment_order(user_id, status, created_at);

explain
select id, order_no, amount
from payment_order
where user_id = 1001
  and status = 1
order by created_at desc
limit 20;

这个索引适合上面的 SQL,因为:

  1. 先按 user_id 缩小到某个用户。
  2. 再按 status 缩小到某类订单。
  3. 最后按 created_at 顺序取最近 20 条。

如果查询变成:

sql
explain
select id, order_no, amount
from payment_order
where status = 1
order by created_at desc
limit 20;

idx_user_status_created 就不一定合适,因为最前面的 user_id 没有条件。联合索引不是三个单列索引,顺序由 B+Tree 的排序方式决定。

验收任务

你应该能讲清:

  1. 为什么 B+Tree 比红黑树更适合数据库。
  2. 为什么 Hash 索引不适合作通用主力索引。
  3. 为什么二级索引查询完整行要回表。
  4. 为什么覆盖索引仍可能慢。
  5. 为什么联合索引 (a,b,c) 不等于三个单列索引。

阶段 3:执行计划和优化器

EXPLAIN 不是背字段

EXPLAIN 是优化器基于 SQL、表结构、索引、统计信息和成本模型推导出的执行计划。它不是普通查询实际执行完后的统计结果。

mermaid
flowchart TD
    A["SQL 文本"] --> B["解析和语义检查"]
    B --> C["读取表结构和索引"]
    C --> D["读取统计信息"]
    D --> E["枚举可能访问路径"]
    E --> F["估算行数、过滤率和成本"]
    F --> G["选择成本较低的计划"]
    G --> H["输出 EXPLAIN 字段"]

必须掌握

字段必须理解详细页
type访问方式,不是越背越好,要结合表大小EXPLAIN
possible_keys候选索引,不代表会用EXPLAIN
key优化器最终选择的索引EXPLAIN
key_len联合索引用到了多少字节EXPLAIN
rows预估扫描行数,来自统计信息EXPLAIN
filtered存储引擎取出后剩余条件预计过滤比例EXPLAIN
Extra额外操作,如排序、临时表、覆盖索引EXPLAIN

Demo:慢 SQL 分析

sql
explain
select id, order_no, amount
from payment_order
where user_id = 1001
  and status = 1
order by created_at desc
limit 100000, 20;

即使使用了覆盖索引,深分页仍可能慢。原因不是“索引没用”,而是数据库仍然要跳过前 100000 条符合条件的索引记录。

优化方式:

sql
select id, order_no, amount
from payment_order
where user_id = 1001
  and status = 1
  and created_at < '2026-07-06 10:00:00'
order by created_at desc
limit 20;

这叫游标分页或基于上次边界的分页。它让数据库从某个确定位置继续向后扫,而不是从头跳过大量记录。

验收任务

你应该能独立回答:

  1. EXPLAIN rows 是真实扫描行数吗?
  2. 为什么统计信息不准会导致优化器选错索引?
  3. 为什么 Using index 不等于一定快?
  4. 为什么 Using filesort 不一定必然慢,但大数据量要警惕?
  5. 如何用慢日志、EXPLAIN ANALYZEoptimizer_trace 交叉验证?

阶段 4:事务、锁、MVCC

为什么事务不是只背 ACID

事务的价值是让一组数据库操作在并发和故障下仍然正确。例如支付成功后:

  1. 更新订单状态。
  2. 写支付流水。
  3. 更新账户余额。
  4. 写消息发送记录。

这些动作不能一半成功一半失败。

mermaid
flowchart TD
    A["支付回调"] --> B["开启事务"]
    B --> C["按订单号查询并加锁"]
    C --> D{"订单是否待支付"}
    D -- "否" --> E["幂等返回"]
    D -- "是" --> F["更新订单已支付"]
    F --> G["写支付流水"]
    G --> H["写本地消息表"]
    H --> I["提交事务"]
    I --> J["异步投递 MQ"]

必须掌握

知识点必须理解原理页
ACID原子性靠 undo,隔离性靠锁/MVCC,持久性靠 redo事务
隔离级别RU、RC、RR、Serializable 的现象差异事务
当前读updatedeletefor update 读最新并加锁锁机制
快照读普通 select 通过 Read View 读可见版本MVCC
行锁InnoDB 行锁加在索引记录上锁机制
间隙锁RR 下范围保护,防止当前读幻读锁机制
死锁多事务互相等待对方锁锁机制
长事务undo 旧版本无法清理,影响 MVCCMVCC

MVCC 和 undo 的关系

mermaid
flowchart TD
    A["当前记录"] --> B["隐藏字段 trx_id"]
    A --> C["隐藏字段 roll_pointer"]
    C --> D["undo log 旧版本 1"]
    D --> E["undo log 旧版本 2"]
    F["Read View"] --> G{"当前版本是否可见"}
    G -- "可见" --> A
    G -- "不可见" --> D

关键结论:

  1. MVCC 不是复制整张表。
  2. 当前记录通过 roll_pointer 找到 undo log 旧版本。
  3. Read View 决定哪个版本对当前事务可见。
  4. undo log 既用于回滚,也用于 MVCC 历史版本。
  5. 长事务会让旧版本不能清理,导致 undo 压力。

验收任务

你应该能做实验:

  1. 开两个会话,事务 A 更新一行不提交,事务 B 更新同一行观察阻塞。
  2. 在 RR 下,事务 A 普通 select 两次,事务 B 中间提交更新,观察快照读一致。
  3. 在同样场景使用 select ... for update,观察当前读和锁等待。
  4. 故意让两个事务按相反顺序更新两行,观察死锁。

阶段 5:undo、redo、binlog

三种日志不要混

日志所属层解决什么问题和事务的关系
undo logInnoDB回滚、MVCC 旧版本事务修改前写旧值
redo logInnoDB崩溃恢复,保证提交不丢事务提交必须保证 redo 可恢复
binlogServer 层主从复制、时间点恢复、审计记录逻辑变更

update 提交流程

mermaid
flowchart TD
    A["UPDATE 命中记录"] --> B["加行锁"]
    B --> C["写 undo 旧版本"]
    C --> D["修改 Buffer Pool 数据页"]
    D --> E["写 redo log record"]
    E --> F["redo prepare"]
    F --> G["写 binlog"]
    G --> H["redo commit"]
    H --> I["事务提交成功"]

为什么 redo 和 binlog 要两阶段提交?

异常点如果没有协调后果
redo 成功,binlog 失败主库恢复有数据,从库没日志主从不一致
binlog 成功,redo 失败从库可能重放,主库恢复没有主从不一致
两者状态不明崩溃恢复无法判断提交状态数据正确性风险

验收任务

你应该能回答:

  1. 为什么事务回滚靠 undo,不靠 redo?
  2. 为什么 redo 是物理页修改日志,binlog 是逻辑变更日志?
  3. 为什么 binlog 能做主从复制,redo 不能直接替代 binlog?
  4. 为什么事务提交成功不代表数据页已经刷盘?
  5. 为什么两阶段提交能保证 redo 和 binlog 一致?

阶段 6:主从、备份、大表治理

主从复制流程

mermaid
flowchart TD
    A["主库提交事务"] --> B["写 binlog"]
    B --> C["从库 IO 线程拉取 binlog"]
    C --> D["写 relay log"]
    D --> E["从库 SQL 线程重放"]
    E --> F["从库数据追上主库"]

主从延迟常见原因:

原因解释处理方向
主库大事务从库必须重放很久拆小事务
从库慢 SQL复制线程被拖慢优化从库资源和查询
写入高峰relay log 堆积并行复制、削峰
网络抖动binlog 拉取慢网络和机房排查
从库资源低CPU/IO 不足升级规格或隔离查询

大表治理决策

mermaid
flowchart TD
    A["单表变慢"] --> B{"是 SQL 和索引问题吗"}
    B -- "是" --> C["优化 SQL、联合索引、分页方式"]
    B -- "否" --> D{"历史数据是否可归档"}
    D -- "是" --> E["冷热归档或分区"]
    D -- "否" --> F{"访问是否可按分片键拆分"}
    F -- "是" --> G["分库分表"]
    F -- "否" --> H["引入搜索、报表库或架构改造"]

分库分表不是慢 SQL 的第一选择。先确认不是索引、深分页、锁等待、统计信息、历史数据未归档造成的慢。

验收任务

你应该能讲清:

  1. 主从复制为什么依赖 binlog。
  2. 写后读从库为什么可能读不到最新数据。
  3. 备份为什么要配合 binlog 做时间点恢复。
  4. 大表为什么不能只靠覆盖索引。
  5. 什么时候归档,什么时候分区,什么时候分库分表。

阶段 7:生产排查

慢 SQL 排查流程

mermaid
flowchart TD
    A["接口慢"] --> B{"SQL 慢还是代码慢"}
    B -- "代码慢" --> C["查接口链路和远程调用"]
    B -- "SQL 慢" --> D["查慢日志"]
    D --> E["用真实参数 EXPLAIN"]
    E --> F{"扫描行数是否大"}
    F -- "是" --> G["查索引、过滤条件、分页"]
    F -- "否" --> H{"是否锁等待或 IO 慢"}
    H -- "锁等待" --> I["查事务和锁"]
    H -- "IO 慢" --> J["查 Buffer Pool、磁盘、回表"]
    G --> K["优化后复测"]
    I --> K
    J --> K

常见现象和判断

现象不要立刻认为应该检查
key 有值但慢索引一定没问题rows、回表、排序、锁等待
Using index 但慢覆盖索引一定快扫描范围、深分页、缓存命中
偶发慢数据库随机坏锁等待、IO 抖动、参数值、缓存未命中
主从延迟只要加从库大事务、从库慢查询、并行复制
死锁数据库异常更新顺序、索引命中、事务范围
连接满数据库连接太少连接泄漏、慢 SQL、事务太长

验收任务

你应该能拿到一条慢 SQL 后完成:

  1. 用真实参数跑 EXPLAIN
  2. 判断 typekeyrowsfilteredExtra
  3. 对比慢日志中的 Rows_examinedRows_sent
  4. 判断是否索引问题、深分页、排序问题、锁等待或 IO 问题。
  5. 给出优化方案,并说明写入成本和风险。

阶段 8:面试表达

面试页只负责标准回答,详细原理必须跳回知识点页。

高频面试题标准回答页深入原理页
MySQL 有几层MySQL 面试架构与请求全过程
为什么用 B+TreeMySQL 面试B+Tree 与索引家族
EXPLAIN rows 怎么来的MySQL 面试EXPLAIN
MVCC 和 undo 关系MySQL 面试MVCC
redo 和 binlog 区别MySQL 面试redo log 与 binlog
覆盖索引仍慢怎么办MySQL 面试大表覆盖索引仍然慢
分库分表怎么设计MySQL 面试分库分表
MySQL容器化怎么保证数据安全MySQL 面试初始化、Volume、备份与升级

面试回答模板

text
我学习 MySQL 不会只背 SQL 语法,而是按业务建模、索引、执行计划、事务并发、日志恢复、主从复制和生产排查这条线理解。

比如一条查询 SQL,先经过连接层认证和权限检查,再由 Server 层解析、预处理、优化器选择执行计划,执行器调用 InnoDB,InnoDB 根据 B+Tree、Buffer Pool、MVCC 和锁读取数据。慢 SQL 排查时不能只看有没有索引,要结合 EXPLAIN 的 type、key、rows、filtered、Extra,以及慢日志、Rows_examined、锁等待和 IO。

比如一条 update,不只是改一行数据,还涉及索引定位、加行锁、写 undo、修改 Buffer Pool、写 redo、写 binlog 和两阶段提交。undo 支持回滚和 MVCC 旧版本,redo 支持崩溃恢复,binlog 支持主从复制和时间点恢复。理解这些流程后,才能解释事务、主从延迟、死锁、大表慢查询和数据恢复问题。

最终验收题

如果下面这些问题都能不看答案讲清,并能回到对应原理页解释流程,MySQL 才算真正进入“能上项目、能面试、能排查”的水平。

问题合格标准
设计订单表字段、约束、索引、状态、金额、时间都合理
解释 B+Tree能从页、树高、范围扫描、叶子链表解释
解释联合索引能用排序规则解释最左前缀
看 EXPLAIN能说明字段来源和优化方向
解释 MVCC能说清当前记录、undo、Read View
解释锁能区分快照读、当前读、行锁、间隙锁
解释 update能串起锁、undo、Buffer Pool、redo、binlog
解释主从能讲 binlog、relay log、重放和延迟
排查慢 SQL能按证据判断扫描、排序、回表、锁、IO
处理大表能在索引、归档、分区、分库分表之间选择

本章小结

“从零到精通”不是看了多少页,而是能不能把业务需求 -> 表设计 -> SQL -> 索引 -> 执行计划 -> 事务并发 -> 日志恢复 -> 主从高可用 -> 生产排查 -> 面试表达串成闭环。

如果某个问题讲不清,不要回去背答案,而是跳到对应知识点页重新看原理和 Demo。真正会 MySQL 的标志,是你能解释数据库为什么这样工作,以及不这样设计会出现什么后果。