redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1, 这样可以保证 MySQL 异常重启之后数据不丢失。 sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。
redo log(重做日志)
「redo log 是 InnoDB 引擎特有的日志」
「《孔乙己》的例子:」
redo log(重做日志)
酒店掌柜有一个粉板,专门用来记录客人的赊账记录。如果赊账的人不多,那么他可以把顾客名和账目写在板上。但如果赊账的人多了,粉板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本。
如果有人要赊账或者还账的话,掌柜一般有两种做法:
一种做法是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
另一种做法是先在粉板上记下这次的账,等打烊以后再把账本翻出来核算。
在生意红火柜台很忙时,掌柜一定会选择后者,因为前者操作实在是太麻烦了。首先,你得找到这个人的赊账总额那条记录。你想想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老花镜慢慢找,找到之后再拿出算盘计算,最后再将结果写回到账本上。
「Redo log不是记录数据页“更新之后的状态”,而是记录这个页 “做了什么改动”。」
WAL
在 MySQL 中如果每次更新操作都要写进磁盘,然后磁盘也要找到那条记录再更新,整个IO成本、查找成本都很高,为了解决这个问题,MySQL的设计者就用到了类似孔乙己中粉板的思路来提升更新效率。
而粉板和账本配合的整个过程,其实就是MySQL里经常说到的WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板(日志),等不忙的时候再写账本(磁盘)。
WAL有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前**「提交的记录都不会丢失」,这个能力称为「crash-safe(碰撞安全)」** 。
两阶段提交
两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案
将redo log
的写入拆成了两个步骤:prepare和commit,这就是"「两阶段提交」"。(参考更新语句执行流程)
「为什么必须有“两阶段提交”呢?」
由于redo log和binlog是两个独立的逻辑,如果不用两阶段提交,要么就是先写完redo log再写binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。
仍然用前面的update语句来做例子。假设当前ID=2的行,字段c的值是0,再假设执行update语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了crash,会出现什么情况呢?
「先写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,与原库的值不同。
可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。
undo log(回滚日志)
在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
当进行数据修改时还会记录undo log,undo log用于数据的撤回操作,它保留了记录修改前的内容。通过undo log可以实现事务回滚,并且可以根据undo log回溯到某个特定的版本的 数据,实现MVCC。
binlog(归档日志)
binlog是Server层的日志
二进制日志(bin log)是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库。