InnoDB的事务和崩溃恢复

欢迎访问原文地址来阅读最新版本
转载请注明出处:https://kang.fun/innodb-transaction

事务隔离级别

1. Read uncommitted

读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。

但是不能避免脏读的问题,即A事务中读取到了B事务中未提交的数据,但是B事务进行了回滚,A事务中这时候就持有了并不存在的脏数据。

2. Read committed

读提交,或者叫读已提交,就是一个事务要等另一个事务提交后才能读取数据。

  1. A事务中读取当前钱包余额为1块钱,并把它增加到2块钱,当事务并未提交
  2. 这时B事务开始执行,将余额从1块钱修改为3块钱,并成功提交事务,当前数据为3块钱
  3. A事务中再次读取,发现余额从1块钱变为3块钱

这就是“不可重复读”问题。也就是说,一次事务中两次读取相同的数据却发现数据不一致了。

3. Repeatable read(Mysql默认)

可重复读,就是在开始读取数据(事务开启)时,不再允许修改操作,但允许写操作。

但是仍旧无法避免幻读问题。

什么是幻读?

假设数据库中订单表数据如下:

id 订单号 金额(元)
3 NO613895797361304528 10.0
5 NO614946192397637269 20.0
6 NO615478237645483920 17.0

现在开启事务A,把id大于等于3且小于等于5的订单数据的支付金额进行求和,即30元。

但事务A尚未提交时,数据库中插入了一条id为4的数据:

id 订单号 金额(元)
4 NO614164929374873483 5.0

那么当事务A提交后,发现id在[3, 5]的范围内,订单金额总额并非30元而是35元,我们称这个现象为幻读

4. Serializable 序列化

Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

如何保证事务 - ACID

为保证事务(transaction)是正确可靠的,数据库引擎必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。

InnoDB分别使用如下方式来保证了这四个特性:

  • redo log重做日志用来保证事务的持久性
  • undo log回滚日志保证事务的原子性
  • undo log + redo log保证事务的一致性
  • 锁(共享、排他)用来保证事务的隔离性

Mysql中三种log的作用

1. binlog

binlog是Mysql Server层级的逻辑变更日志,它记录了数据库的所有变更,以二进制存储在磁盘上。

主要作用在两个方面:

  • 增量备份

  • 主从复制

2. redo log

redo log是InnoDB存储引擎层的日志,记录的是新数据的备份,在事务提交前将redo log持久化。

数据写入时,实际上是写入了内存中的Buffer Pool,Buffer Pool中的数据根据策略定期写入磁盘。那么就带来一个问题,如果在写入磁盘前系统崩溃了,数据一致性难以保证,所以在事务完成前会写入redo log,系统崩溃重启后,通过redo log来恢复未持久化到页中的数据。

这样还能使写入变为一个异步操作,那么在写入磁盘前,数据库引擎还能对多次同一个键的修改进行合并,降低磁盘写入内容和次数。

3. undo log

事务中的每一次修改,innodb 都会先记录对应的 undo log 记录。与 redo log 用于数据的灾后重新提交不同,undo log 主要用于数据修改的回滚。

与 redo log 记录的是物理页的修改不同,undo log 记录的是逻辑日志。

在事务中每次更新记录后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被数据的roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。

当事务回滚时,通过undo log对数据反向更新。

InnoDB保证事务隔离性的方式 - MVCC(多版本并发控制)

多版本的意思就是一条数据在数据库中同时存在多个版本,在某个事务对其进行操作的时候,需要查看这一条记录的隐藏列事务版本id,比对事务id并根据事务隔离级别去判断读取哪个版本的数据。

隐藏属性 是否必须 描述
row_id 行ID,唯一标识一条记录(如果定义主键,则没有)
transaction_id 事务ID
roll_pointer DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本,形成版本链

事务持久化策略

1. steal / no-steal

是否允许一个uncommitted的事务将修改更新到磁盘

  • steal策略

    那么此时磁盘上就可能包含uncommitted的数据,因此系统需要记录undo log,以防事务abort时进行回滚(roll-back)

  • no steal策略

    就表示磁盘上不会存在uncommitted数据,因此无需回滚操作,也就无需记录undo log

2. force / no-force

事务在committed之后是否将所有更新立刻持久化到磁盘

  • force策略

    在committed之后必须将所有更新立刻持久化到磁盘

  • no-force策略

    在committed之后可以不立即持久化到磁盘,而是缓存更新批量持久化到磁盘

no-force的优点是提升顺序写,缺点是有可能在crash后导致事务数据丢失,所以需要记录redo log,系统重启后进行前滚(roll-forward)操作

现在DBMS常用的是steal/no-force策略,因此一般都需要记录redo log和undo log。这样可以获得较快的运行时性能,代价就是在数据库恢复(recovery)的时候需要做很多的事情,增大了系统重启的时间。

你可能感兴趣的:(数据库,数据库)