mysql的更新过程涉及到两个重要的日志模块redo log和binlog
mysql> create table t(id int primary key, c int);
update t set c=c+1 where id=2;
在mysql中如果每一次更新都需要写磁盘的话,然后磁盘需要找到那条记录,然后再更新整个IO成本,查询成本都很高,所以Innodb引擎就会把记录先写到redo log里面,并更新内存,同时Innodb会在适当的时候把操作记录写到磁盘里面,往往在系统空闲的时候这么做。
这里写到redo log也是写到磁盘的,但是和更新过程不同的是,更新是在磁盘上随机IO,而写redo log是顺序IO,效率更高。
如果需要更新的数据很多,redo log大小不够了,需要将之前的日志更新到数据文件。有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。也就是mysql 和 innodb是独立分开的 mysql重启对在innodb中的redo log 不影响。
redo log是Innodb引擎特有的日志,而mysql server层也有自己的日志binlog(归档日志)
这两种日志的区别
redo log是Innodb引擎特有的,binlog是Server层的,所有引擎都可以用
redo log是物理日志,记录在数据页做什么修改,binlog是逻辑日志比如给id=2的记录的c字段-1
redo log是循环写的,binlog是追加写的,文件大小写完之后,下个文件继续写,不覆盖
执行器和innodb执行更新操作的流程
执行器从引擎找到id=2这一行,如果在内存中直接返回给执行器
执行器给这行数据加1,调用引擎写入新数据
更新新数据到内存,并写入redo log,此时redo log处于prepare阶段,告知执行器执行完成
执行器生成这个操作的binlog,把binlog写入磁盘
执行器调用引擎的提交事务接口,redo log处于comit阶段,结束
注意这里的prepare和comit都是redo log的状态不是事务状态。
为什么需要两阶段提交
1.先写redo log再写binlog,已经写完redo log但是mysql异常重启,binlog还没写完,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。恢复出来c的值是0
2.险些binlog再写redo log如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
两阶段提交,这是为了让两份日志之间的逻辑一致