图解 MySQL 01|一条 SQL 怎么跑完:Server 层和存储引擎层那条缝
MySQL 被讲成「一个数据库」,但它其实是 Server 层 + 存储引擎层两块代码拼出来的。本文聊聊那条缝在哪里、为什么这么设计,以及看懂它能解决什么问题。
从一条 SQL 的执行路径开始,拆到连接器、优化器、索引、事务、锁、日志、复制和备份恢复。
适合想系统补齐 MySQL 底层机制、排查路径和工程取舍的后端工程师。
先看一条 SQL 怎么穿过 Server 层和存储引擎。
理解 redo、binlog、undo、MVCC 和隔离级别。
把表锁、行锁、间隙锁和死锁排查连起来看。
从 B+ 树、回表、联合索引到执行计划。
Buffer Pool、排序、分组、JOIN 和临时表。
主从复制、延迟、备份和 PITR 闭环。
MySQL 被讲成「一个数据库」,但它其实是 Server 层 + 存储引擎层两块代码拼出来的。本文聊聊那条缝在哪里、为什么这么设计,以及看懂它能解决什么问题。
连接器被讲成「建立 TCP 连接 + 鉴权」,但出问题最多的不是建立而是断开。本文聊聊 wait_timeout 和 interactive_timeout 这对孪生参数,以及连接池配置的三个常见坑。
explain 看到的执行计划,跟实际跑出来的有时对不上。本文聊聊分析器、优化器、执行器三个组件怎么协作,以及优化器为什么会选错索引。
redo log 被讲成「崩溃恢复日志」,但它的精髓不是日志本身,而是 WAL(Write-Ahead Logging)思想。本文聊聊 redo log 的循环写结构、两阶段 checkpoint,以及为什么 kill -9 不丢数据。
binlog 被讲成「归档日志」,但它的真正角色是 MySQL 主从复制和数据恢复的协议层。本文聊聊 binlog 跟 redo log 的三大差别、三种 format 的坑,以及为什么 8.0 默认改用 ROW。
redo log 和 binlog 是两个独立日志,怎么保证要么都写成功要么都不写?答案就是两阶段提交。本文聊聊 prepare / binlog / commit 三步流程,以及崩溃恢复时 InnoDB 怎么判断事务到底有没有提交。
undo log 被讲成「回滚日志」,但它的真正价值是承载 MVCC 的多版本。本文聊聊 undo log 怎么工作、三种操作各自的 undo 形态,以及大事务为什么会让 undo 段涨爆。
MVCC 被讲成「多版本并发控制」,但它的核心是一个叫 ReadView 的小结构。本文聊聊快照读和当前读的差别、ReadView 怎么判断版本可见性,以及 RC 和 RR 隔离级别的真正分野。
隔离级别被讲成「读未提交 / 读已提交 / 可重复读 / 串行化」的名词解释,但真正理解它要看 SQL 标准和 InnoDB 实现的差别。本文聊聊三种异常、四个级别,以及为什么 MySQL 默认 RR。
MySQL 的锁被讲成「行锁」,但它其实是三层粒度——全局锁、表级锁、行级锁。本文聊聊每层的具体类型、应用场景,以及为什么 mysqldump 用 FTWRL 而不是 readonly。
行锁被讲成「锁住一行」,但 InnoDB 实际有三种格式——Record 锁单行、Gap 锁间隙、Next-Key 锁区间。本文聊聊三种格式的差别、加锁规则,以及为什么 SELECT WHERE id=10 可能锁住 id=15。
间隙锁被讲成「防幻读的机制」,但它的实际行为很反直觉——表里没数据也能锁住一个范围,两个事务插入不同 ID 也能死锁。本文聊聊间隙锁的诡异行为、死锁原理,以及怎么减轻它的影响。
死锁不是数据库坏了,而是事务等待图里出现了环。本文聊聊死锁和锁等待的区别、SHOW ENGINE INNODB STATUS 怎么读、performance_schema 怎么辅助定位,以及生产中怎么修和兜底。
索引不是“加速查询”的魔法,而是一套让磁盘 IO 可控的数据组织方式。本文聊聊哈希、有序数组、二叉树和 B+ 树的差别,以及为什么 InnoDB 最终选择 B+ 树。
InnoDB 的主键索引叶子节点存整行,二级索引叶子节点存主键值。本文聊聊聚簇索引、二级索引、回表,以及为什么主键设计会影响整张表。
联合索引不是把多个字段随手拼在一起。本文聊聊覆盖索引、最左前缀、字段顺序和排序复用,解释为什么索引设计本质上是查询模式设计。
索引下推让存储引擎在遍历索引时先判断部分 where 条件,减少无意义回表。本文聊聊 ICP 解决什么问题、什么时候生效,以及 explain 里的 Using index condition 怎么看。
普通索引和唯一索引不只是约束不同,它们的写入路径也不同。本文聊聊 change buffer 为什么只适合普通二级索引,以及写多读少场景下怎么取舍。
优化器不是凭直觉选索引,而是根据统计信息估算成本。本文聊聊基数、采样、rows 估算、ANALYZE TABLE,以及为什么执行计划有时会选错。
EXPLAIN 不是背字段含义,而是还原 MySQL 准备怎么访问数据。本文聊聊 type、key、rows、Extra 的阅读顺序,以及如何用执行计划定位慢查询方向。
Buffer Pool 是 InnoDB 最核心的内存结构。本文聊聊数据页如何进入内存、LRU 为什么要分冷热区,以及为什么大查询可能冲击缓存命中率。
更新先改内存页并写 redo,数据页稍后刷盘。本文聊聊脏页、checkpoint、刷脏触发条件,以及为什么写入高峰会出现性能抖动。
count(*) 看起来只是数行,但 InnoDB 不能像 MyISAM 一样直接返回表总行数。本文聊聊 count(*)、count(1)、count(列) 的差别,以及大表计数怎么做。
ORDER BY 可能复用索引顺序,也可能进入 filesort。本文聊聊索引排序、排序缓冲、全字段排序和 rowid 排序,以及分页为什么越翻越慢。
GROUP BY 的核心是把同一组的数据聚到一起。本文聊聊索引分组、临时表聚合、Using temporary,以及如何减少大分组查询的代价。
JOIN 慢不慢,取决于驱动表、被驱动表索引和连接算法。本文聊聊 Nested Loop Join、Batched Key Access、Hash Join,以及多表查询的优化方向。
临时表是 MySQL 执行复杂查询时的中间工作台。本文聊聊内部临时表何时出现、为什么会从内存转磁盘,以及如何降低临时表成本。
主从复制不是简单“拷贝数据”,而是从库拉取主库 binlog 后重放。本文聊聊 binlog dump、relay log、SQL 线程、GTID,以及复制链路的关键风险。
读写分离提升吞吐,也会引入主从延迟。本文聊聊延迟来源、Seconds_Behind_Source 的局限、并行复制,以及业务上怎么做读一致性。
备份不是把文件存起来,而是能在事故时恢复到目标时间点。本文聊聊逻辑备份、物理备份、binlog 增量恢复、恢复演练,以及图解 MySQL 系列的最终收束。