一文搞懂回滚和持久化

redo log

事务的支持是数据库区分文件系统的重要特征之一,事务的四大特性:

  • 原子性:所有的操作要么都做,要么都不做,不可分割。
  • 一致性:数据库从一种状态变成另一种状态的的结果最终是一致的,比如A给B转账500,A最终少了500,B最终多了500,但是A+B的值始终没变。
  • 隔离性:事务和事务之前相互隔离,互不干扰。
  • 持久性:事务一旦提交,它对数据的变更是永久性的。

本篇文章主要说说持久性相关的知识。

当我们在事务中更新一条记录的时候,比如:

update user set age=11 where user_id=1;
复制代码

它的流程大概是这样的:

  1. 先判断user_id这条数据所在的页是否在内存里,如果不在的话,先从数据库读取到,然后加载到内存中
  2. 修改内存中的age为11
  3. 写入redo log,并且redo log处于prepare状态
  4. 写入binlog
  5. 提交事务,redo log变成commit状态

这里面有几个关键的点:redo log是什么?为什么需要redo log?prepare状态的redo log是什么?redo log和binlog是否可以只选其一...?带着这一系列的问题,我们来揭开redo log的面纱。

为什么要先更新内存数据,不直接更新磁盘数据?

我们为什么不每次更新数据的时候,直接更新对应的磁盘数据?首先我们知道磁盘IO是缓慢的,内存是快速的,两者的速度不是一个量级的,那么针对缓慢的磁盘IO,出现了索引,通过索引哪怕数据成百上千万我们依然可以在磁盘上很快速的找我们的数据,这就是索引的作用。但是索引也需要维护,并不是一成不变的,当我们插入一条新数据A的时候,由于这条数据要插入在已存在的数据B之后,那么就要移动B数据,让出一个位置给A,这个有一定的开销。更糟糕的是,本来要插入的页已经满了,那么就要申请一个新的页,然后挪一部分数据过去,这叫做页的分裂,这个开销更大。如果我们的sql变更是直接修改磁盘的数据,恰巧正好出现上面的问题,那么此时的效率就会很低,严重的话会造成超时,这也是上面更新的过程为什么先要加载对应的数据页到内存中,然后先更新内存中的数据的原因。对于mysql来说,所有的变更都必须先更新缓冲池中的数据,然后缓冲池中的脏页会以一定的频率被刷入磁盘(checkPoint机制),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。

为什么需要redo log?

缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkpoint机制可以保证数据的最终落盘,然而由于checkpoint并不是每次变更的时候就触发的,而是master线程隔一段时间去处理的。所以最坏的情况就是刚写完缓冲池,数据库宕机了,那么这段数据就是丢失的,无法恢复。这样的话就不满足ACID中的D,为了解决这种情况下的持久化问题,InnoDB引擎的事务采用了WAL技术(Write-Ahead Logging),这种技术的思想就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log。当发生宕机且数据未刷到磁盘的时候,可以通过redo log来恢复,保证ACID中的D,这就是redo log的作用。

redo log是如何实现的?

redo log的写入并不是直接写入磁盘的,redo log也有缓冲区的,叫做redo log buffer(重做日志缓冲),InnoDB引擎会在写redo log的时候先写redo log buffer,然后也是以一定的频率刷入到真正的redo log中,redo log buffer一般不需要特别大,它只是一个临时的容器,master线程会每秒将redo log buffer刷到redo log文件中,因此我们只要保证redo log buffer能够存下1s内的事务变更的数据量即可&

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值