redo log(重做日志)
首先,明确redo log是innodb存储引擎层面的日志文件,当我们对数据进行更改操作之后,innoDB会将记录先记录在redo log中,然后更新内存,这样我们的更改操纵也就算完成了,然后存储引擎会在一个合适的时机,将更改数据写入磁盘,这个时间一般在比较空闲的时候。
redo log是固定大小的,意思就是我们不能无限的向redo log里面写记录,当写满的时候,我们需要擦除redo log,redo log是循环输入的,我们可以将它理解为一个循环队列,一个标志位叫write pos 表示当前记录的位置上,另一个标志位是check point 表示当前要擦除的位置,可以先简单理解为一个环形跑道,两个人在追逐,当write pos 追上check point时就表示满了,要擦除redo log ,但是在擦除之前要将这些记录更新到数据文件
接下来想redo log 替我们完成了一个什么功能,换一句话就是InnoDB引擎利用redo log实现了即使数据库发生了异常重启,提交的记录也不会丢失,我们可以根据redolog记录恢复,这个能力称为crash-safe
binlog(归档日志)
Mysql整体可以分为两部分,一层是server层,另一层就是存储引擎层,redo log是存储引擎层面的日志,而binlog就是server层的日志,为什么引入两个日志?
原因是MySQL支持插件式存储引擎,最开始MySQL默认是MyISAM存储引擎,但是这个存储引擎不支持crash-safe,也就是说binlog只能用于归档使用不支持crash-safe,所以只能引入redo log 日志系统来实现这个功能
Undo log
undo log有两个作用:提供回滚和多个行版本控制(MVCC)。
在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。
undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
注意:undo log是逻辑日志,可以理解为实现记录中的记录就能抵消产生这条记录的操作(相反):
– 当delete一条记录时,undo log中会记录一条对应的insert记录
– 当insert一条记录时,undo log中会记录一条对应的delete记录
– 当update一条记录时,它记录一条对应相反的update记录
比较redo log 和 binlog的不同
- redo log 是innoDB引擎特有的,binlog是server层的所有引擎都能使用
- redo log是物理日志,记录的是“某个数据页数据发生的变化”,binlog是逻辑日志,记录的是语句的原始逻辑
- redo log是循环写的,空间固定会用完,binlog是可以追加写入的,不会被覆盖,写到一定大小就会切换到下一个,并不会覆盖以前的日志
redo log 两阶段提交
首先我们来分析一个update语句的执行流程, update T set c=c+1 where ID=2;如图所示
执行流程:
1、执行器先从引擎中找到数据,如果在内存中直接返回,如果不在内存中,查询后返回
2、执行器拿到数据之后会先修改数据,然后调用引擎接口重新吸入数据
3、引擎将数据更新到内存,同时写数据到redo中,此时处于prepare阶段,并通知执行器执行完成,随时可以操作
4、执行器生成这个操作的binlog
5、执行器调用引擎的事务提交接口,引擎把刚刚写完的redo改成commit状态,更新完成
因为redo log 和binlog是两个独立的逻辑,如果不用两阶段提交,那么就会出现两种情况,一种是先写完redo log 然后写完binlog,另一种是顺序反过来。我们来分析一下这两种情况
先写 redo log 后写 binlog
假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句,然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
先写 binlog 后写 redo log
如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案,所以说为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。