比如我们有一条sql如下:
update A set c=c+1 where id = 1;
这条语句到底怎么执行的呢,其实更新语句走的也是我们查询语句那一套流程,但是更新语句会有一个操作,就是会在更新的同时清空A表的查询缓存。与查询流程不同,更新的时候还会设计两个重要的日志,分别是binlog和redolog。
redoLog
redolog其实是一个环,上面会有两个指针,其中一个记录checkpoint
当前擦除的位置,一个记录write pos
当前写入的位置。
根据上面的描述的话,也就是在我们做更新操作的时候不是直接去操作磁盘上面的数据,而是我们先写日志,然后再系统空闲的情况下会去写磁盘,这个技术也被称为WAL技术。
这样有几个好处:
- 我们写入日志都是顺序追加写的,所以会比随机IO效率高很多;
- 因为在做更新的的时候一般都会把数据查询到buffer_pool中,会优先对buffer_pool的数据进行更新并记录redolog,然后更新结束,此时在buffer_pool中的页就是脏页,直到刷入磁盘才算彻底完成,这种先写内存的方式和redis的AOF很像,降低每次更新的资源消耗;
- 当我们在某一时刻宕机的时候,我们因为有了redolog可以对数据进行快速的恢复,避免丢失大量数据。
下面举个例子,可能更能说明redolog
比如说我们现在在玩单机游戏,但是我们现在上班中,总不能摸鱼去不停地玩吧,总要看看老板在不在,恰巧今天老板特别爱溜达,那你是不是就不能一直在写,这时候问题来了,如果我们电脑性能不行,你退出的时候没有保存你当前进度,那不是白玩,所以这时候如果有一个内存,能让你暂时的保存你的游戏进度是不是就很好,然后在某一刻也能定期的去永久的保存的你进度,完全就避免了我这段时间付出的努力和辛苦被丢失的风险嘛。
binlog、redolog以及undolog
首先redolog就不再赘述了,其实他主要就是参与操作内存,并将数据暂时写入buffer_pool中,然后在一定时间进行更新。
binlog介绍
binlog主要是物理层面的,有三个级别分别是mixed,row,statement,具体这三种都是做什么的,接下来我也不进行详细介绍,网上的介绍很多,binlog的作用其实就是记录数据库及数据的每一次变更,当然这个还是取决于日志级别的不同记录的内容相对也不同。
在原始状态下,mysql是没有inoodb引擎的,而内置的myisam引擎没有redolog,所以不支持crash_safe,只有binlog归档日志,所以不能保证数据丢失的风险,于是inoodb为了解决这个问题,便引入了redolog日志系统。
那怎么理解这两个log的作用,这个时候我们就不得不提到一条SQL DML语句的执行器运行过程了,如下图:
我们能看到在事务开始的时候我们首先写入的是redolog,当我们执行commit之前的时候才会记录到binlog,如果在redolog进入prepare的时候,断电了,这个时候也不能保留redolog的日志,也要进行回滚,所以总的来说,redolog保证了crash_safe。
binlog和redolog的不同
- binlog属于server层的归档日志,redolog只有inoodb特有,只有存在事务才支持redolog;
- binlog属于物理日志,比如只会记录你的银行余额改成200,redolog属于逻辑日志,他会记录你的银行记录是从100改成200;
- redolog是循环写的,日志大小固定,binlog是追加写,大小不固定,写到一定大小后会创建新文件继续写。
两阶段提交(2PC)
通过上面的图我们能看到在提交阶段redolog经历了两阶段提交,即第一阶段为prepare阶段,只有当数据写入binlog的时候们才会进行commit操作,保证了数据的最终一致性。
为什么需要两阶段提交呢?
先写redolog再写binlog
在日常情况下,这个自然不会有问题,但是如果我们某一刻突然crash,这个时候我们要对数据进行恢复,那么我们在redolog里面记录了你的余额变成了200,但是binlog里面还是记录你的余额是100,那么我们使用binlog的备份日志进行数据还原的时候数据就会变成100,导致数据不正确。
先写binlog再写redolog
如果先写binlog那么余额从100变成200的时候,binlog对其进行了相应的记录,但是redolog在记录的时候,机器crash住了,那么redolog便会触发回滚,事务无效,也就是真实状态应该是余额不变,仍是100,但是这个时候如果我们需要备份文件去恢复数据,其实binlog记录的是200,导致数据不一致。
所以归根结底还是为了保证数据的最终一致性。