一、体系结构
1. 一条 SQL 的旅程
- 连接器:认证、权限、连接数
- 解析器:词法语法,生成语法树
- 优化器:选索引,定 JOIN 顺序
- 执行器:调引擎接口,逐行过滤
- InnoDB:Buffer Pool 到 16KB 页
2. 内存结构
- Buffer Pool:给物理内存 50 到 70%
- Change Buffer:只服务非唯一二级索引
- Log Buffer:默认 16M
- LRU 冷热分区:防全表扫描污染
3. 磁盘结构
- 表空间、段、区 1MB、页 16KB、行
- 行溢出阈值约 8KB,超出走溢出页
4. 引擎对比
- InnoDB:事务、行锁、MVCC
- MyISAM:表锁
- Memory:易失
二、索引
1. 为什么是 B+ 树
- 哈希:不支持范围与排序
- 红黑树:树高约 24 层
- B 树:非叶子存数据,树更高
- B+ 树:矮胖、叶子有序、IO 稳定
- 三层约 2000 万行,最多 3 次 IO
2. 聚簇与二级索引
- 聚簇索引:叶子存整行,一表一个
- 二级索引:叶子存索引列加主键
- 回表:两次 B+ 树查找
- 覆盖索引:Extra 显示 Using index
3. 最左前缀
- 范围查询后,后续列只能 ICP 过滤
- 跳过最左列,完全用不上
4. 索引失效
- 列上函数或运算
- 左模糊 like %x
- 隐式类型转换:varchar 不加引号
- OR 一侧无索引
- 取反、不等于、NOT IN,区分度太低
5. 设计方法
- 选择性 10% 以上才有意义
- 等值在前、范围在后、排序跟上
- 索引不超过 5 到 6 个,前缀索引不能覆盖
6. 自增主键优势
- 顺序写,不页分裂
- 二级索引更小,整型比较更快
三、事务、MVCC 与锁
1. ACID 实现
- 原子性:undo log
- 隔离性:MVCC 加锁
- 持久性:redo log 加 double write
2. 隔离级别
- 默认 RR
- RC:每条语句新建 ReadView
- RR:事务内复用 ReadView
- RR 幻读:快照读 MVCC,当前读临键锁
3. MVCC
- 隐藏字段 TRX_ID 与 ROLL_PTR
- undo 版本链,顺链找旧版本
- ReadView 四字段:m_ids 与 min、max
4. 锁
- 行锁锁索引项,不是行本身
- 记录锁、间隙锁、临键锁,唯一等值只加记录锁
- MDL 导致 DDL 排队雪崩
- 无索引,退化为锁全表
5. 死锁
- 交叉加锁成环
- 回滚代价小的事务
- 排查 INNODB STATUS 与 data_lock_waits
- 预防:统一顺序、短事务、唯一索引
6. 长事务危害
- undo 不释放,History list 膨胀
- 锁持有久,拖慢主从
- MDL 阻塞 DDL
四、日志系统
1. redo log
- 物理日志,循环写
- WAL:随机写变顺序写,且 crash-safe
- 8.0.30 起 redo_log_capacity 统一控制
- 容量太小,checkpoint 频繁,周期卡顿
2. undo log
- 事务回滚
- MVCC 版本链
3. binlog
- Server 层,逻辑日志,追加写
- ROW 格式,主从绝对一致
- 用于复制与 PITR
4. 两阶段提交
- redo prepare,写 binlog,再 commit
- binlog 完整则提交,否则回滚
5. double write
- 防页写一半导致的页损坏
- 代价约一倍写放大
6. 双 1 配置
- flush_log_at_trx_commit 设为 1
- sync_binlog 设为 1
- 日志类可降为 2 加 100 换吞吐
五、优化一:系统层
1. CPU
- 8 到 16 核覆盖多数 OLTP,超 32 核收益递减
- 主频决定单条复杂 SQL
2. 内存
- Buffer Pool 占 50 到 70%
- swap 必须为 0,换页即雪崩
3. 磁盘
- NVMe 优于 SATA SSD,SATA SSD 优于 HDD
- RAID10,不用 RAID5,写惩罚重
- XFS 加 noatime,调度器 none,关 THP
4. 检查手段
- iostat 与 iotop 看 util、await 与进程
- fio 测 IOPS 与延迟
- 优先级:SSD 大于内存,内存大于 CPU
六、优化二:配置层
1. 内存参数
- buffer_pool_size 最重要的一项
- 实例数 8,每个至少 1G
- tmp_table_size 与 heap 必须相等
2. 连接参数
- max_connections 按连接池总数算
- max_used_connections 超 80% 才扩容
- wait_timeout 收 300 到 600
3. 刷盘参数
- redo 4 到 8G,太小会周期卡顿
- flush_method 用 O_DIRECT
- io_capacity 2000 到 4000,flush_neighbors 设 0
4. 参数模板
- 4C8G:BP 5G,连接 500,redo 2G
- 16C64G:BP 40G,连接 1000,redo 8G
5. 调参纪律
- 一次只改一个参数
- 先预发后生产,留版本记录
七、优化三:SQL 与索引
1. 定位四步
- 慢日志与 ps digest 找慢 SQL
- EXPLAIN 看执行计划
- 判断瓶颈:行数、回表、排序
2. EXPLAIN 关键字段
- type 从 const 到 ALL,越左越好
- key 为 NULL 说明优化器放弃索引
- rows 与 filtered 估扫描量
- Extra 看 Using index 与 filesort
- EXPLAIN ANALYZE 看实际耗时
3. 典型改写
- 深分页:延迟关联或游标法
- 大事务拆批,批量插入 500 到 1000 行一批
- 避免 SELECT *,破坏覆盖索引
- JOIN 被驱动表必须有索引
- ORDER BY 与 GROUP BY 走索引
- 大表 DDL 用 INSTANT 或 gh-ost
- COUNT 走最小二级索引或计数表
八、优化四:架构与运维
1. 主从复制
- 主库 binlog 到 dump 到 relay 到回放
- GTID 主从切换必备
- 半同步 AFTER_SYNC 不丢数据
- 并行复制 LOGICAL_CLOCK
2. 主从延迟
- 从库规格低于主库
- 大事务、无主键表、从库读压力
- 用 pt-heartbeat 替代旧指标
3. 读写分离
- 写主读从
- 写后短时强行走主库
- ProxySQL 或 ShardingSphere
4. 高可用
- MHA 已停更
- MGR 基于 Paxos,官方推荐
- 三个坑:脑裂、丢数据、应用不知切换
5. 备份恢复
- XtraBackup 物理热备首选
- mysqldump single-transaction 逻辑备
- binlog 做 PITR,按时间点恢复
- 延迟从库防误删,每月演练
6. 监控告警
- 连接数超 80%,BP 命中率低于 99%
- 慢查询增速、锁等待、History list
- 主从延迟超 10 秒,磁盘超 80%
九、面试速答
1. 回答公式
一句结论、两点机制、一句取舍。
2. 最高频八问
- 一条 SQL 的执行流程
- 为什么用 B+ 树
- 回表与覆盖索引
- MVCC 与 ReadView
- RR 能否防幻读
- 两阶段提交为什么需要
- 慢 SQL 怎么排查
- 深分页与主从延迟怎么治
十、数字与信号
- 16KB 页,三层 B+ 树约 2000 万行
- 50 到 70% 给 Buffer Pool
- 双 1 保证不丢数据
- 99% 命中率警戒线
- type 为 ALL 且 rows 大,等于没走索引