MySQL 核心知识梳理:从底层原理到实战优化
- [MySQL 核心知识梳理:从底层原理到实战优化](#MySQL 核心知识梳理:从底层原理到实战优化)
-
- [1. MySQL 整体架构](#1. MySQL 整体架构)
- [2. InnoDB 存储引擎(目前默认,也是重点)](#2. InnoDB 存储引擎(目前默认,也是重点))
- [3. Buffer Pool ------ InnoDB 的内存加速器](#3. Buffer Pool —— InnoDB 的内存加速器)
- [4. 日志系统:Redo Log vs Binlog](#4. 日志系统:Redo Log vs Binlog)
-
- [4.1 Redo Log(重做日志,引擎层)](#4.1 Redo Log(重做日志,引擎层))
- [4.2 Binlog(归档日志,Server 层)](#4.2 Binlog(归档日志,Server 层))
- [4.3 为什么要两个日志?](#4.3 为什么要两个日志?)
- [5. 索引](#5. 索引)
-
- [5.1 B+ 树索引(InnoDB 默认)](#5.1 B+ 树索引(InnoDB 默认))
- [5.2 自适应哈希索引(AHI)](#5.2 自适应哈希索引(AHI))
- [5.3 其他索引](#5.3 其他索引)
- 索引使用原则(面试高频)
- [6. 事务与隔离级别](#6. 事务与隔离级别)
-
- [ACID 回顾](#ACID 回顾)
- 四种隔离级别
- [7. MVCC(多版本并发控制)](#7. MVCC(多版本并发控制))
- [8. 锁机制](#8. 锁机制)
-
- [8.1 锁粒度](#8.1 锁粒度)
- [8.2 锁类型](#8.2 锁类型)
- [8.3 死锁](#8.3 死锁)
- [9. SQL 优化实战(常见思路)](#9. SQL 优化实战(常见思路))
-
- [9.1 慢查询定位](#9.1 慢查询定位)
- [9.2 索引失效场景](#9.2 索引失效场景)
- [9.3 分页优化](#9.3 分页优化)
- [9.4 写优化](#9.4 写优化)
- [10. 常用运维与监控](#10. 常用运维与监控)
- 写在最后
MySQL 核心知识梳理:从底层原理到实战优化
一篇尽量系统但不说废话的 MySQL 知识地图,帮助你理解它为什么快、怎么保证不丢数据、以及如何写出高效的 SQL。
1. MySQL 整体架构
MySQL 分为 Server 层 和 存储引擎层,这种分层设计让它支持多种存储引擎(InnoDB、MyISAM、Memory 等)。
客户端连接
↓
连接器(管理连接、权限验证)
↓
查询缓存(8.0 已移除)→ 解析器(词法/语法分析)→ 优化器(选择索引、决定 join 顺序)→ 执行器(调用存储引擎接口)
↓
存储引擎(默认 InnoDB)
↓
磁盘文件(.ibd、.frm、binlog、redo log 等)
- Server 层:负责连接管理、查询解析、优化、执行,以及 binlog、慢查询日志等。
- 引擎层:负责数据的实际存储与读取,提供 ACID 事务、行锁、MVCC 等特性。
2. InnoDB 存储引擎(目前默认,也是重点)
InnoDB 是一个兼顾高可靠和高性能的通用存储引擎,适用于绝大多数 OLTP 场景。
核心特性
- ACID 事务:支持原子性、一致性、隔离性、持久性。
- 行级锁:默认使用行锁,并发性能远高于表锁。
- 多版本并发控制(MVCC):实现非阻塞读,读写不冲突。
- 外键约束:保证数据完整性。
- Crash Safe:崩溃自动恢复(通过 Redo Log 和 Undo Log)。
- 聚簇索引:数据和主键索引存储在一起,二级索引回表。
物理存储结构(了解即可)
- 表空间(Tablespace) :逻辑存储单元,包括系统表空间、独立表空间(
.ibd)、通用表空间等。 - 段(Segment):数据段、索引段、回滚段。
- 区(Extent):1 区 = 64 个连续页(默认 1MB)。
- 页(Page):默认 16KB,是 InnoDB 读写的最小单位。
3. Buffer Pool ------ InnoDB 的内存加速器
Buffer Pool 是 InnoDB 的 内存核心,所有的数据读写优先经过它。
- 作用:缓存磁盘上的数据页(包含表数据、索引、插入缓冲、自适应哈希索引等)。
- 加速原理:读 -- 先从 Buffer Pool 命中,否则从磁盘读取并缓存;写 -- 先修改 Buffer Pool 中的页,标记为"脏页",后台线程择机刷盘。
- 数据结构 :
free list:空闲页LRU list:已被使用的页,采用 改进 LRU(分成 young 区和 old 区,防止全表扫描污染缓存)flush list:脏页列表,按修改时间排序
- 配置建议 :
innodb_buffer_pool_size设为服务器物理内存的 50%~75%;高并发下可设置innodb_buffer_pool_instances拆分成多个实例以降低争用。
4. 日志系统:Redo Log vs Binlog
4.1 Redo Log(重做日志,引擎层)
- 记录内容:物理日志,例如 "在表空间 X、页号 Y、偏移量 Z 处将值 A 改成 B"。
- 写入方式:固定大小、循环写入(写满后会覆盖旧的日志)。
- 作用 :保证事务的持久性,即 Crash-Safe 能力。事务提交时,Redo Log 先行落盘,即使宕机,重启后也能根据 Redo Log 重放未完成的页修改。
- 相关参数 :
innodb_log_file_size、innodb_log_files_in_group。
4.2 Binlog(归档日志,Server 层)
- 记录内容 :逻辑日志,例如
UPDATE t SET c = c+1 WHERE id = 1。 - 写入方式:追加写,文件写满后切换下一个文件,不会覆盖。
- 作用 :
- Point-in-Time 恢复:利用 binlog 回放恢复到任意时刻。
- 主从复制:从库订阅主库的 binlog 实现数据同步。
- 格式:STATEMENT(记录 SQL)、ROW(记录行变更,推荐)、MIXED。
4.3 为什么要两个日志?
- 早期 MySQL 默认引擎 MyISAM 没有 Crash-Safe 能力,靠 binlog 无法恢复。
- InnoDB 加入后,为了实现自己的崩溃恢复,引入了 Redo Log;同时为了兼容 binlog 的复制/恢复机制,保留两者。
- 两阶段提交 :保证 Redo Log 和 Binlog 逻辑一致,避免主从数据不一致。过程:
- Prepare:Redo Log 写入并标记为 prepare 状态。
- Commit:Binlog 写入成功 → 将 prepare 的 Redo Log 改为 commit 状态。
5. 索引
5.1 B+ 树索引(InnoDB 默认)
- 聚簇索引(主键索引):叶子节点存储完整行数据。
- 二级索引(辅助索引):叶子节点存储主键值,查询数据需要"回表"。
- 为什么用 B+ 树?
- 矮胖:树高通常 3~4 层,适合磁盘 IO。
- 叶子节点双向链表,支持范围扫描。
- 非叶子节点只存索引,一个页可放更多键。
5.2 自适应哈希索引(AHI)
- InnoDB 自动为热点索引页在内存中构建哈希索引,只针对等值查询。
- 优点:O(1) 定位,比 B+ 树更快。
- 缺点:消耗内存、高并发下可能有争用。
- 参数:
innodb_adaptive_hash_index(默认 ON)。
5.3 其他索引
- 全文索引:
MATCH AGAINST支持,适用于文本搜索。 - 空间索引:用于地理数据(GIS)。
索引使用原则(面试高频)
- 最左前缀原则 :联合索引
(a,b,c),查询条件必须从 a 开始,才能用到该索引。 - 覆盖索引:select 的字段都在索引中,可避免回表。
- 索引下推(ICP):在索引遍历过程中,提前过滤掉不符合条件的记录,减少回表次数。
- 不要建冗余/重复索引。
6. 事务与隔离级别
ACID 回顾
- Atomicity(原子性):通过 Undo Log 实现。
- Consistency(一致性):由数据库约束 + 业务逻辑保证。
- Isolation(隔离性):由锁 + MVCC 实现。
- Durability(持久性):通过 Redo Log 实现。
四种隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ UNCOMMITTED | ✅ 可能 | ✅ 可能 | ✅ 可能 |
| READ COMMITTED | ❌ | ✅ 可能 | ✅ 可能 |
| REPEATABLE READ(MySQL 默认) | ❌ | ❌ | ❌(InnoDB 用间隙锁解决了) |
| SERIALIZABLE | ❌ | ❌ | ❌ |
- READ COMMITTED 和 REPEATABLE READ 都是通过 MVCC 实现的快照读。
- REPEATABLE READ 下,普通的
SELECT不会加锁,而是读取快照版本;加锁读(SELECT ... FOR UPDATE)会使用间隙锁防止幻读。
7. MVCC(多版本并发控制)
MVCC 使 InnoDB 在加锁的情况下实现 非阻塞读。
- 核心思想:每行数据可能有多个版本(通过 Undo Log 串联)。
- 每个事务启动时,会生成一个 Read View,包含当前活跃事务 ID 列表。
- 可见性判断:根据版本的事务 ID 与 Read View 比较,决定该行是否对当前事务可见。
- 不同隔离级别的 Read View 生成时机不同:
- RC:每次查询都生成新的 Read View → 会出现不可重复读。
- RR:只在事务第一次查询时生成 Read View → 保证重复读一致。
8. 锁机制
8.1 锁粒度
- 表锁 :
LOCK TABLES ...或 MyISAM 默认锁。 - 行锁:InnoDB 默认,通过索引实现,不是直接锁行,而是锁索引记录。
- 间隙锁(Gap Lock):锁定一个范围(不包含记录本身),防止幻读。
- 临键锁(Next-Key Lock):行锁 + 间隙锁,是 RR 级别下默认的锁。
8.2 锁类型
- 共享锁(S) :
SELECT ... LOCK IN SHARE MODE - 排他锁(X) :
SELECT ... FOR UPDATE、UPDATE、DELETE - 意向锁:表级锁,表示事务想要在表中某行加 S 或 X 锁,用于快速判断表锁是否可行。
8.3 死锁
- 两个或多个事务相互持有对方需要的锁。
- InnoDB 会自动检测死锁,回滚代价较小的事务(可通过
innodb_deadlock_detect控制)。
9. SQL 优化实战(常见思路)
9.1 慢查询定位
- 开启慢查询日志:
slow_query_log=1,设置long_query_time。 - 使用
EXPLAIN分析执行计划,关注:type:从好到差依次是system>const>eq_ref>ref>range>index>ALL。possible_keys/key:实际使用的索引。rows:预估扫描行数。Extra:Using filesort、Using temporary是优化重点。
9.2 索引失效场景
- 对索引列使用函数、表达式(如
WHERE DATE(create_time) = '2025-01-01')。 - 隐式类型转换(如
varchar字段与int比较)。 - LIKE 以
%开头('%abc')。 - OR 条件中,部分字段没有索引(除非覆盖所有 OR 字段)。
9.3 分页优化
- 避免
LIMIT 1000000,10→ 会扫描前 100 万行再丢掉。 - 改进方法:
- 延迟关联:
SELECT * FROM t JOIN (SELECT id FROM t ORDER BY id LIMIT 1000000,10) tmp ON t.id = tmp.id - 记住上一页最大 ID:
WHERE id > last_id ORDER BY id LIMIT 10(适用于主键自增且无删除)。
- 延迟关联:
9.4 写优化
- 批量插入:
INSERT INTO ... VALUES (1,2),(3,4)...比单条快很多。 - 适当调整
innodb_flush_log_at_trx_commit:1(默认)------ 每次提交都刷盘,最安全。0/2------ 牺牲一点持久性,提升写入性能。
10. 常用运维与监控
SHOW ENGINE INNODB STATUS:查看 InnoDB 内部状态(锁、事务、缓冲池等)。SHOW PROCESSLIST:查看当前连接与执行语句。performance_schema和sys库:用于诊断锁等待、查询性能。pt-query-digest:分析慢查询日志的工具。
写在最后
MySQL 的知识体系非常庞杂,但只要你理清 架构分层 、InnoDB 存储引擎 、索引 、日志 、事务与锁 这几条主线,剩下的细节都可以按图索骥。
如果能亲手在本地搭建一个 MySQL,结合 EXPLAIN 和 SHOW ENGINE 去观察 SQL 的行为,会比只看文章深刻得多。
如果你对某个点(比如 MVCC 底层、Redo Log 的组提交、Online DDL 原理等)想深入细节,也可以告诉我,我再单独展开。
希望这篇梳理对你有帮助。