MySQL 存储引擎及隔离级别
大约 5 分钟
1. 存储引擎
1.1 InnoDB
MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎
InnoDB实现了四个标注的隔离级别:
- 可重复读(REPEATABLE READ):默认,同一事务中,每次读取的数据相同。通过多版本并发控制(MVCC)+ 间隙锁(Next-Key Locking)防止幻影读
- 提交读(READ COMMITTED):
- 未提交读(READ UNCOMMITTED):可以读到未提交事务的修改
- 可串形化(SERIALIZABLE):通过加读写锁的方式来避免并行访问
1.2 MyISAM
MyISAM 不支持事务,设计简单,数据以紧密格式存储
优点:
- 资源占用低
缺点:
- 不支持行级锁,只能对整张表加锁。
- 不支持事务
- 不支持外键约束
2. 读写冲突
2.1 Dirty Write脏写
脏写是两个事务同时操作一个记录,当事务A在开启和提交之间,事务B开启,并在事务A提交后进行了回滚,此时A表现为写入失败(记录未改变):
- 原值为20
- A事务开启,随后B事务开启,两事务分别读取到目前的值为20
- A事务修改为22,B事务修改为25
- A事务提交,原值变为22
- B事务回滚,原值变为B读取的20
2.2 Dirty Read脏读
脏读是事务A读到了事务B修改但还未提交的数据
- 原值为20
- B事务修改值为25
- A事务读取到值为25
- B事务回滚,A事务读取的即为脏数据
2.3 No Repeatable Read不可重复读
不可重复读是事务A在执行过程中,多次读到事务B修改但未提交的数据,结果都不一样
- 原值为20
- A事务第一次读为20
- B事务修改为25,但并未提交
- A事务第二次读为25
2.4 Phantom Read幻读
幻读是事务A在执行过程中,第一次读到记录后,事务B又插入了一条记录,事务A第二次读记录后,发现记录数量发生了变化
- 原表仅有
Tom
- A事务第一次读取为
Tom
- B事务插入
Bob
- A事务第二次读取为
Tom、Bob
2.5 四种隔离级别
上述的四种问题,其严重程度:脏写 > 脏读 > 不可重复读 > 幻读,为此InnoDB提供了四种隔离级别
因为脏写非常恶劣,所以每一个隔离级别都确保了不会发生脏写
Dirty Write 脏写 | Dirty Read 脏读 | No Repeatable Read 不可重复读 | Phantom Read 幻读 | |
---|---|---|---|---|
READ UNCOMMITTED 未提交读 | 不可能 | 可能 | 可能 | 可能 |
READ COMMITTED 已提交读 | 不可能 | 不可能 | 可能 | 可能 |
REPEATABLE READ 可重复读 | 不可能 | 不可能 | 不可能 | 可能 |
SERIALIZABLE 串行化 | 不可能 | 不可能 | 不可能 | 不可能 |
3. InnoDB的数据页存储
3.1 数据页组成
数据库的IO最小单位是页,InnoDB的数据页默认是16KB。也就说每次读写都以16KB为单位加载/刷入磁盘。数据页的各部分如下:
- 文件头(File Header):表示页的信息,存在上下数据页的指针,使其形成双向链表
- 页头(Page Header):表示页的状态信息
- 最小和最大记录(Infimum+Supremum):记录页中最小记录和最大记录
- 用户记录(User Records):存储行记录
- 空闲空间(Free Space):未使用的空间
- 页目录(Page Directory):存储用户记录的相对位置,对记录起索引作用
- 文件尾(File Tailer):校验页的完整性
3.2 页目录
数据页中的数据按照主键顺序形成单向链表。在最差的情况下,数据需要完整遍历一遍所有节点才能完成检索。数据页中的页目录对记录实现了索引的效果,页目录和记录的关系如下:
- 页目录将包括最小最大记录在内的记录划分成组,并在每组的最后一条记录(也就是最大记录)上记录组内数量
n_owned
- 页目录指向了每组的最后一条记录的地址偏移量,也称之为槽(slot)
- 页目录通过二分查找具体的行
- 为了避免单分组内的数据过多导致效率下降,InnoDB作出了如下规定:
- 第一个分组记录只能有1条
- 最后一个分组记录只能是1-8条
- 其余分组记录可以是4-8条