事务是数据库区别于文件系统的重要特征之一.
提到事务,首先想到的就是 ACID:
A: 原子性(atomicity)
C: 一致性(consistency)
I: 隔离行(isolation)
D: 持久性(durability)
事务的作用: 事务会把数据库从一种一致的状态转换为另一种一致状态。
oracle和sql server的默认隔离级别(read committed),不满足隔离性的要求.
但mysql InnoDB的默认隔离级别(read repeatable),完全满足ACID特性.
为什么满足?请看我之前写的另外一篇文章, Mysql数据库事务的隔离级别和锁的实现原理分析
持久性保证事务系统的高可靠性(high reliability) 而不是高可用性(high availability),高可用性需要其他组件配合一起完成.
那么上述事务的特性是如何实现的呢?本文带着这些问题,翻阅了相关资料,整理成了如下的这篇文章.
要么都执行,要么都回滚,InnoDB最常用,最常见的事务.
事务的操作过程有 begin, A, B, C, D, commit 几个过程,那么带有保存点的扁平事务过程大致如下:
begin--> 隐含保存点1(save work 1)-->A-->B(save work2)-->C-->D(rollback work2) -->commit
上述过程中如果遇到rollback work2, 只需要回滚到保存点2,不需要全部回滚.
简单来说,带有保存点的扁平事务就是有计划的回滚操作。
保存点是容易失的(volatile), 而非持久的.系统崩溃,所有保存点都将丢失.
链事务提交一个事务时,释放不需要的数据对象,将必要的上下文传递给下一个要开始的事务. 下一个事务可以看到上一个事务的结果.
带有保存点的偏平事务可以回滚到任意正确的保存点,链事务只能回滚到当前事务.
扁平全程持锁,链事务在commit后释放锁.
链事务如: T1->T2->T3
可以理解为一颗事务树,顶层事务控制着下面的子事务. 所有的叶子节点是扁平事务,实际工作是由叶子节点完成的.
分布式环境下运行的扁平事务.
InnoDB支持上述除嵌套事务以外的所有事务类型.
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, 将一个日志记录的
由此我们可以知道: 重做日志的写入并不完全是顺序的,因为除了log block的写入外,有时还需要更新前2KB部分的信息.
undo log 用来保证事务的一致性. undo 回滚行记录到某个特定版本,undo 是逻辑日志,根据每行记录进行记录.
undo 存放在数据库内部的undo段,undo段位于共享表空间内.
undo 只把数据库逻辑的恢复到原来的样子.
undo日志除了回滚作用之外, undo 实现MVCC,读取一行记录时,发现事务锁定,通过undo恢复到之前的版本,实现非锁定读取.
InnoDB有很多日志,
日志中有2个概念需要分清楚,逻辑日志和物理日志.
有关操作的信息日志成为逻辑日志.
比如,插入一条数据,undo逻辑日志的格式大致如下:
undo日志总是这样:
1). insert操作,则记录一条delete逻辑日志.
2). delete操作,则记录一条insert逻辑日志.
3). update操作,记录相反的update,将修改前的行改回去.
binlog(二进制日志)就是典型的逻辑日志,而事务日志(redo log)则记录的物理日志,他们的区别是什么呢?
1, redo log 是在存储引擎层产生的,binlog是在数据库上层的一种逻辑日志,任何存储引擎均会产生binlog.
2, binlog记录的是sql语句, 重做日志则记录的是对每个页的修改.
3, 写入的时间点不一样. binlog 是在事务提交后进行一次写入,redo log在事务的进行中不断的被写入.
4, redo log 是等幂操作(执行多次等于执行一次,redo log 记录
redo log 是可能是多条记录, 如:
既有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, 物理日志记录的是修改页的的详情,逻辑日志记录的是操作语句. 物理日志恢复的速度快于逻辑日志.