数据库01:MySQL

从 InnoDB 引擎、B+Tree 索引结构、MVCC 到主从复制,拆解 MySQL 高并发读写背后的设计原理和常见调优方向。

字数 1382 阅读时长 ≈ 4 分钟 2026-7-14 2026-7-14
数据库01:MySQL

MySQL 是互联网公司最常用的关系型数据库,没有之一。很多项目从一开始就用 MySQL,然后在业务增长中不断遇到性能瓶颈、锁冲突、主从延迟等问题。

这篇文章围绕四个核心点:InnoDB 引擎架构、B+Tree 索引、MVCC 并发控制、主从复制,把 MySQL 的设计逻辑和常见坑点讲清楚。

InnoDB 引擎:为什么是默认选择

MySQL 5.5 之后,InnoDB 成为默认存储引擎,不是因为它最完美,而是因为它在事务支持、并发控制、崩溃恢复这三件事上做得最平衡。

引擎事务支持锁粒度崩溃恢复适用场景
InnoDBACID行级锁支持绝大多数业务场景
MyISAM不支持表级锁不支持只读、统计报表
Memory不支持表级锁不支持临时表、缓存

InnoDB 的核心架构可以分成三层:

  1. Buffer Pool:内存缓存,存放热点数据和索引页,减少磁盘 IO
  2. Log Buffer + Redo Log:保证事务持久性,先写日志再写磁盘
  3. Undo Log + Purge:支持 MVCC 和事务回滚

关键设计:WAL(Write-Ahead Logging)。事务提交时,先写 Redo Log,再更新 Buffer Pool,最后后台线程异步刷盘。这样即使崩溃,重启后可以通过 Redo Log 恢复。

B+Tree 索引:为什么快,为什么慢

索引是 MySQL 性能的命门。理解 B+Tree 才能真正明白”为什么这条 SQL 慢”。

B+Tree 结构特点

  • 多路平衡树:每个节点可以有多个子节点,树的高度很低(通常 3-4 层)
  • 叶子节点链式连接:范围查询只需要遍历叶子节点链表
  • 非叶子节点不存数据:只存索引键,减少磁盘 IO

索引类型对比

索引类型结构优点缺点
主键索引B+Tree,叶子节点存整行数据查询最快只能有一个
普通索引B+Tree,叶子节点存主键值灵活需要回表(除覆盖索引)
联合索引B+Tree,按索引顺序排列覆盖多条件最左前缀原则
唯一索引B+Tree,键值唯一保证唯一性插入更新开销大

最左前缀原则

联合索引 (a, b, c) 能匹配的查询条件:

  • WHERE a = ?
  • WHERE a = ? AND b = ?
  • WHERE a = ? AND b = ? AND c = ?
  • WHERE b = ? ✗(跳过最左列)
  • WHERE a = ? AND c = ? ✗(中间列缺失)

索引失效场景

  • 索引列上使用函数:WHERE DATE(create_time) = ?
  • 隐式类型转换:WHERE id = '123'(字符串转数字)
  • 范围查询后列无法使用索引:WHERE a = ? AND b > ? AND c = ?(c 无法使用)
  • NOT IN / NOT LIKE / != 通常不走索引

MVCC:读写不冲突的魔法

MVCC(Multi-Version Concurrency Control)是 InnoDB 实现高并发的关键,它让读操作不加锁,写操作只锁必要的行。

版本链与 Read View

每个事务看到的数据版本由两个因素决定:

  1. Undo Log 版本链:每次更新都会生成一条 Undo Log,形成版本链
  2. Read View:事务开始时创建的快照,包含当前活跃事务 ID

隔离级别与一致性视图

隔离级别脏读不可重复读幻读实现方式
READ UNCOMMITTED可能可能可能读最新数据
READ COMMITTED不可能可能可能每次查询生成新 Read View
REPEATABLE READ不可能不可能不可能事务开始时生成 Read View
SERIALIZABLE不可能不可能不可能加锁

MySQL 默认隔离级别是 REPEATABLE READ,通过间隙锁(Gap Lock)解决了幻读问题。

快照读 vs 当前读

  • 快照读SELECT,不加锁,读历史版本
  • 当前读SELECT ... FOR UPDATEINSERTUPDATEDELETE,加锁,读最新版本

主从复制:读写分离的基础

主从复制是 MySQL 水平扩展的标配,核心思路是”写主库,读从库”。

复制流程

  1. Binlog Dump:主库记录变更到 Binlog
  2. IO Thread:从库 IO 线程拉取 Binlog 到 Relay Log
  3. SQL Thread:从库 SQL 线程回放 Relay Log

复制方式

方式同步性延迟适用场景
异步复制异步毫秒级绝大多数场景
半同步复制至少一个从库确认略高对数据一致性要求高
并行复制多线程回放更低高写入场景

常见问题

主从延迟:写多读少、大事务、DDL 操作都会导致延迟。解决方案:

  • 小事务,避免长时间锁表
  • 并行复制,提高从库回放速度
  • 监控延迟,超过阈值自动切主

数据不一致:网络抖动、从库宕机可能导致数据丢失。解决方案:

  • 半同步复制,确保至少一个从库落盘
  • 定期数据校验(pt-table-checksum)
  • 从库只读,防止误写

常见调优方向

硬件层面

  • SSD 代替 HDD,随机写性能提升 10 倍以上
  • 足够的内存,让热点数据全在 Buffer Pool
  • 高速网络,减少主从复制延迟

配置层面

  • innodb_buffer_pool_size:通常设为物理内存的 50-70%
  • innodb_log_file_size:1-2GB,太大恢复慢,太小切换频繁
  • innodb_flush_log_at_trx_commit:1=安全,2=性能(宕机可能丢 1 秒数据)

架构层面

  • 读写分离,主库写,从库读
  • 分库分表,突破单库容量上限
  • 缓存层(Redis),减少数据库压力

项目判断清单

  • 单表数据量超过 1000 万 → 考虑分表或索引优化
  • QPS 超过 1 万 → 考虑读写分离或缓存
  • 经常出现慢查询 → 先看执行计划,再优化索引
  • 主从延迟超过 1 秒 → 检查 Binlog 大小和从库配置
  • 事务冲突频繁 → 减少事务粒度,优化锁竞争