浅析Mysql InnoDB存储引擎事务原理

一 事务的分类

1.1 扁平事务

要么都执行,要么都回滚,InnoDB最常用,最常见的事务.

1.2 带有保存点的偏平事务

事务的操作过程有 begin, A, B, C, D, commit 几个过程,那么带有保存点的扁平事务过程大致如下:

begin--> 隐含保存点1(save work 1)-->A-->B(save work2)-->C-->D(rollback work2) -->commit

上述过程中如果遇到rollback work2, 只需要回滚到保存点2,不需要全部回滚. 

简单来说,带有保存点的扁平事务就是有计划的回滚操作。
保存点是容易失的(volatile), 而非持久的.系统崩溃,所有保存点都将丢失.

1.3 链事务 

链事务提交一个事务时,释放不需要的数据对象,将必要的上下文传递给下一个要开始的事务. 下一个事务可以看到上一个事务的结果.

带有保存点的偏平事务可以回滚到任意正确的保存点,链事务只能回滚到当前事务. 

扁平全程持锁,链事务在commit后释放锁. 

链事务如:  T1->T2->T3

1.4 嵌套事务

 可以理解为一颗事务树,顶层事务控制着下面的子事务.  所有的叶子节点是扁平事务,实际工作是由叶子节点完成的.

1.5 分布式事务  

分布式环境下运行的扁平事务. 

InnoDB支持上述除嵌套事务以外的所有事务类型.



二 事务ACID的实现

2.1 隔离性的实现

事务的隔离性由存储引擎的锁来实现, 详细见   Mysql数据库事务的隔离级别和锁的实现原理分析

2.2 原子性和持久性的实现

redo log 称为重做日志(也叫事务日志),用来保证事务的原子性和持久性.   

redo恢复提交事务修改的页操作,redo是物理日志,页的物理修改操作.

事务的提交过程如下图: 


当提交一个事务时,实际上它干了如下2件事:

一:  InnoDB存储引擎把事务写入日志缓冲(log buffer),日志缓冲把事务刷新到事务日志.

二:  InnoDB存储引擎把事务写入缓冲池(Buffer pool).

这里有个问题, 事务日志也是写磁盘日志,为什么不需要双写技术?

因为事务日志块的大小和磁盘扇区的大小一样,都是512字节,因此事务日志的写入可以保证原子性,不需要doublewrite技术

重做日志缓冲是由每个为512字节大小的日志块组成的. 日志块分为三部分:  日志头(12字节),日志内容(492字节),日志尾(8字节).

重做日志是一个日志组,下面的日志形式描绘了这一可能的重做日志存储方式:

group1: 
每个log group的第一个 redo log file,需要保存: log_file_header 512字节,checkpoint1 512字节,空 512字节,checkpoint2 512字节.
group1: 
redo log file1:  log_file_header,cp1,空,cp2 log block,log block.......log block.
read log file2:  空,空,空,空,log block,log block,.......log block. 

group2: 
redo log file1:  log_file_header,cp1,空,cp2 log block,log block.......log block.
read log file2:  空,空,空,空,log block,log block,.......log block. 

但有些事务需要跨 log block如何提交磁盘,如事务A 重做日志是 712字节,需2个log block来装.

InnoDB采用的是group commit的方式来保证原子性.

log buffer什么时候会把block刷新到磁盘呢? 一般是下面的时刻:

1, 事务提交时

2, log buffer 内存使用到一半时.

3, log checkpoint时。

checkpoint表示已经刷新到磁盘上的重做日志总量,因此恢复时只需要恢复从checkpoint开始的日志部分.

<数据库系统概念> 454页<事务管理>一节中说到: 

检查点的引入是为了解决,系统恢复时,需要搜索整个日志来做redo和undo 操作.

系统周期性的执行检查点,刷新检查点时需要执行以下动作序列:

1, 将当前位于主存的所有日志记录输出到磁盘上(我的理解是事务日志).

2, 将当前修改了的缓冲块(我的理解是脏页)输出到磁盘上.

3, 将一个日志记录的<checkpoint>输出到磁盘(我的理解是事务日志).

由此我们可以知道:  重做日志的写入并不完全是顺序的,因为除了log block的写入外,有时还需要更新前2KB部分的信息.

2.3 一致性的实现

undo log 用来保证事务的一致性. undo 回滚行记录到某个特定版本,undo 是逻辑日志,根据每行记录进行记录.

undo 存放在数据库内部的undo段,undo段位于共享表空间内.

undo 只把数据库逻辑的恢复到原来的样子.

undo日志除了回滚作用之外, undo 实现MVCC,读取一行记录时,发现事务锁定,通过undo恢复到之前的版本,实现非锁定读取.


三 InnoDB的日志

InnoDB有很多日志,

日志中有2个概念需要分清楚,逻辑日志和物理日志.

3.1 逻辑日志 

有关操作的信息日志成为逻辑日志.

比如,插入一条数据,undo逻辑日志的格式大致如下: 

<Ti,Qj,delete,U> Ti表示事务id,U表示Undo信息,Qj表示某次操作的唯一标示符

undo日志总是这样:

1).  insert操作,则记录一条delete逻辑日志. 

2).  delete操作,则记录一条insert逻辑日志.

3).  update操作,记录相反的update,将修改前的行改回去.

3.2 物理日志

新值和旧值的信息日志称为物理日志. <Ti,Qj,V> 物理日志 

binlog(二进制日志)就是典型的逻辑日志,而事务日志(redo log)则记录的物理日志,他们的区别是什么呢?

1, redo log 是在存储引擎层产生的,binlog是在数据库上层的一种逻辑日志,任何存储引擎均会产生binlog.

2, binlog记录的是sql语句, 重做日志则记录的是对每个页的修改.

3, 写入的时间点不一样. binlog 是在事务提交后进行一次写入,redo log在事务的进行中不断的被写入.

4, redo log 是等幂操作(执行多次等于执行一次,redo log 记录<T0,A,950>记录新值,执行多少次都一样) , binlog 不一样;

redo log 是可能是多条记录, 如: 

<T0,start> 

<Action1> ..... <ActionN>

<t0,commit> 

既有start,又有commit 才是一条完整的redo log。才会被执行,缺失commit在恢复时是不会被执行的.

如遇到并发写入,则redo log 还有可能是如下的情况:
T1,T2,T1,*T2,T3,T1,*T3,*T1 
带*的是事务提交的时间. (从左到右的时间顺序)

redo log ,每个事务对应多个日志条目. 重做日志是并发写入的. 无顺序.

binlog,则如下:
T1,T4,T3,T2,T8,T6,T7,T5


重做日志的例子: 
表t:  a(int,primary key)
 b(int,key(b))
 
insert into t select 1,2;
重做日志大概为(页的物理修改操作,若涉及到B+树的split,会更多的记录):
page(2,3),offset 32,value 1,2 # 主键索引
page(2,4),offset 64,value 2   # 辅助索引.

Mysql存储引擎在启动时,会进行恢复操作:
重做日志记录的是物理日志,因此恢复的速度比逻辑日志,如二进制日志要快很多.


四 总结

        1, redo log(事务日志)保证事务的原子性和持久性(物理日志)

        2, undo log保证事务的一致性,InnoDB的MVCC也是用undo log来实现的(逻辑日志).

        3, redo log中带有有checkPoint,用来高效的恢复数据.

        4, 物理日志记录的是修改页的的详情,逻辑日志记录的是操作语句. 物理日志恢复的速度快于逻辑日志.

猜你喜欢

转载自blog.csdn.net/xubo_ob/article/details/71079361