三、MySQL
MySQL 事务 ACID
MySQL 事务的 ACID
一、ACID 是什么
| 特性 | 英文 | 含义 |
|---|---|---|
| A | Atomicity | 原子性 |
| C | Consistency | 一致性 |
| I | Isolation | 隔离性 |
| D | Durability | 持久性 |
二、Redo Log 和 Undo Log
| 日志 | 类型 | 作用 |
|---|---|---|
| Redo Log | 物理日志 | 保证持久性 |
| Undo Log | 逻辑日志 | 保证原子性 + MVCC |
三、Redo Log
核心思想
使用:
- WAL(Write Ahead Log)
原则:
- 先写日志
- 再写磁盘
四、Undo Log
作用:
- 回滚事务
- MVCC 多版本控制
Redo Log 刷盘策略
innodb_flush_log_at_trx_commit
| 配置值 | 刷盘策略 | 安全性 | 性能 |
|---|---|---|---|
| 1 | 每次提交立即刷盘 | 最高 | 最低 |
| 0 | 每秒刷盘 | 最低 | 最高 |
| 2 | 每次提交写 OS Cache | 折中 | 折中 |
面试回答
- 1 最安全
- 0 性能最好
- 2 是平衡方案
生产环境:
- 一般使用 1
MySQL 隔离级别
MySQL 四种隔离级别
| 隔离级别 | 英文 | 问题 |
|---|---|---|
| 读未提交 | Read Uncommitted | 会脏读 |
| 读已提交 | Read Committed | 会不可重复读 |
| 可重复读 | Repeatable Read | 默认级别 |
| 串行化 | Serializable | 性能最差 |
MySQL 默认隔离级别
text
Repeatable Read
即:
- 可重复读
脏读、不可重复读、幻读
一、脏读
定义:
- 读到未提交数据
二、不可重复读
定义:
- 两次查询结果不同
原因:
- 中间被其他事务修改
三、幻读
定义:
- 两次查询记录数量不同
原因:
- 中间有插入/删除
幻读解决方案
InnoDB 如何解决幻读
一、快照读
使用:
- MVCC
看到:
- 历史快照
二、当前读
使用:
- 间隙锁(Gap Lock)
作用:
- 锁定范围
- 防止插入
三、总结
InnoDB:
- MVCC + 间隙锁
解决:
- 幻读问题
四、补充
串行化级别:
- 也能解决幻读
缺点:
- 性能差
MVCC
MVCC 原理
一、核心思想
MVCC:
text
Multi-Version Concurrency Control
即:
- 多版本并发控制
二、Read View 可见性规则
可见
- 小于最小活跃事务 ID
- 已提交事务
不可见
- 活跃事务列表中的事务
- 大于当前事务 ID
三、简单理解
可见:
- 当前事务开始前已经提交的数据
不可见:
- 未提交数据
- 未来事务数据
索引下推(ICP)
索引下推是什么
索引下推:
text
Index Condition Pushdown
MySQL 5.6 引入。
核心思想
把:
- WHERE 条件中
- 能利用索引过滤的部分
下推到:
- 存储引擎层
提前过滤数据。
作用
减少:
- 回表次数
提高:
- 查询性能
三、常见问题
1)深度分页
错误方案:
sql
LIMIT 1000000, 20
优化方案
使用:
sql
WHERE id > last_id
即:
- 游标分页
2)回表过多
问题:
- 先查索引
- 再查数据页
解决方案
使用:
- 覆盖索引
3)COUNT 慢
解决:
- 汇总表
- 从库统计
4)索引维护成本高
经验:
- 超过 5000 万建议分表
四、分表建议
推荐数量
text
64 ~ 128
张表。
每张表数据量
text
150万 ~ 300万
最佳。
五、复杂查询优化
方案:
- Elasticsearch
- ClickHouse
六、生产经验
优化后:
- 简单查询几十毫秒
长期方案:
- 分表更稳妥
MySQL 数据页为什么是 16KB
核心原因
16KB:
- 是磁盘 IO
- 与内存利用率
之间的平衡。
一、磁盘层面
现代文件系统:
text
4KB
为基础块。
16KB:
- 正好是 4 倍
减少:
- 磁盘寻道
二、索引层面
B+Tree:
- 一个节点对应一个页
16KB 的优势
约能存:
text
1000 条索引记录
三层树容量
约:
text
10 亿数据
三、内存层面
如果页太大:
- 小查询浪费内存
如果页太小:
- 树高度增加
为什么不建议修改
原因:
- 兼容性问题
- 生产验证不足