一 事务的分类
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, 物理日志记录的是修改页的的详情,逻辑日志记录的是操作语句. 物理日志恢复的速度快于逻辑日志.