redolog

解决的问题:

如果每一次更新操作都写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程IO成本、查找成本都很高,为了解决这个问题,采用类似“酒店老板的粉板”思路来提升更新效率。

  • WAL技术:Write-Ahead Logging, 先写日志,再写磁盘,类似,先写粉板,再写账本。

当一条记录需要更新的时候,InnoDB会先写redo log,并更新内存,这个时候更新就算完成了,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往在系统比较空闲的时候做,这就像酒店打烊了以后掌柜做的事情。
如果“粉板”写满了,掌柜只能放下手里的活,把粉板中的一部分数据更新到账本中, 然后把这些记录从粉板上擦掉,为记录新账腾出时间。

redo log 是固定大小的,比如可以配置一组4个文件,每个文件大小是1GB。从头开始写,写道末尾又回到开头循环写。write-pos记录当前的位置。

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录也不会丢失,这个能力称为crash-safe.

binlog

Mysql分为:Server层和引擎层

server层:主要做Mysql功能层面的事情 引擎层:负责存储相关的事情

redolog属于InnoDB引擎特有的日志,而Server层也有自己的日志,成为binlog(归档日志)。

为什么需要两份日志?

最初的Mysql没有InnoDB引擎,只有MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用来归档。

这两种日志有三个不同:

  • redo_log是InnoDB引擎特有的,binlog是mysql的Server层实现的,所有引擎都可以使用
  • redo_log是物理日志,记录的是“在某个数据页上做了什么修改 ”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一行的c字段加1.
  • redo log是循环写的,空间固定会用完;binlog是可以追加写的,追加写的意思binlog写到一定大小会切换到下一个,并不会覆盖以前的日志。

基于此,来看更新的逻辑

更新逻辑

update T set c=c+1 where ID = 2;

  1. 执行器先找到引擎获取ID=2这一行,ID是主键,引擎直接树搜索找到这一行,如果ID=2一行的数据页本来就在内存中,就直接返回给执行器,否则,需要先从磁盘读取到内存,然后返回。
  2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,并将更新记录写到redo log里面,此时的redo log处于prepare的状态,然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的binlog, 并把binlog写入磁盘。
  5. 执行器调用引擎提交事务接口,引擎把刚刚写入的redolog改为提交(commit)状态。

    两阶段提交

    原因:为了让两份日志之间的逻辑保持一致。
  • 怎么恢复半个月内任一秒的状态?

    binlog。

如果是不是两阶段提交,会出现什么问题?

  1. 先写redo log再写binlog。假设redo log写完,binlog没写完,mysql重启。redolog写完即使系统崩溃,也能恢复这一行的值是1.但是binlog没记录,会丢失一次更新。
  2. 如果先写binlog再写redolog。binlog写完,redolog没写,崩溃以后这个事务无效,但是binlog记录了,之后binlog恢复的时候就会多出来一个事务,恢复出来和原库的值不相同。

简单来说,redo log和binlog都可以表示事务的状态,两阶段提交就是让这两个状态保持逻辑上的一致。

小结

物理日志redolog和逻辑日志binlog
redolog用于保证crash-safe的能力。