跳至主要內容

MySQL 存储引擎及隔离级别

pptg大约 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
Dirty Write脏写
Dirty Write脏写

2.2 Dirty Read脏读

脏读是事务A读到了事务B修改但还未提交的数据

  • 原值为20
  • B事务修改值为25
  • A事务读取到值为25
  • B事务回滚,A事务读取的即为脏数据
Dirty Read脏读
Dirty Read脏读

2.3 No Repeatable Read不可重复读

不可重复读是事务A在执行过程中,多次读到事务B修改但未提交的数据,结果都不一样

  • 原值为20
  • A事务第一次读为20
  • B事务修改为25,但并未提交
  • A事务第二次读为25
No Repeatable Read不可重复读
No Repeatable Read不可重复读

2.4 Phantom Read幻读

幻读是事务A在执行过程中,第一次读到记录后,事务B又插入了一条记录,事务A第二次读记录后,发现记录数量发生了变化

  • 原表仅有Tom
  • A事务第一次读取为Tom
  • B事务插入Bob
  • A事务第二次读取为Tom、Bob
Phantom Read幻读
Phantom Read幻读

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条