电子说
redo日志位于MySQL数据目录下,默认有ib_logfile0
和ib_logfile1
两个文件,如下图所示。
可以发现,两个redo日志文件的大小都是50331648,默认48MB。为什么这个大小是固定的呢?因为如果我们要使用顺序I/O,就必须在申请磁盘空间的时候一次性决定申请的空间大小,这样才能保证申请的磁盘空间在地址上的连续性。
这也就决定了redo日志的旧数据会被覆盖,一旦文件被写满,就会触发Buffer Pool脏页到磁盘的同步,以腾出额外空间记录后面的修改。
可以通过以下指令查看redo日志的系统属性。
mysql> show variables like 'innodb_log%';
+-----------------------------+----------+
| Variable_name | Value |
+-----------------------------+----------+
| innodb_log_buffer_size | 16777216 |
| innodb_log_checksums | ON |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_log_write_ahead_size | 8192 |
+-----------------------------+----------+
参数名称 | 含义 |
---|---|
innodb_log_file_size | 指定每个redo日志文件的大小,默认48MB |
innodb_log_files_in_group | 指定redo日志文件的数量,默认2 |
innodb_log_group_home_dir | 指定redo文件的路径,如果不指定,则默认为datadir目录 |
介绍到这里,读者朋友可以发现,我们刚才探索的是如何让已经提交的事务保持持久化,但是如果某些事务偏偏在执行到一半的时候出现问题怎么办?
事务的原子性要求事务中的所有操作要么都成功,要么都失败,不允许存在中间状态。就好比我在写这篇文章的时候,会时不时地敲一下ctrl+Z
返回到上一步或者过去好几步之前的状态,MySQL也需要“留一手”,把事务回滚时需要的东西都记录下来。
比如,插入数据的时候,至少应该把新增的这条记录的主键的值记录下来,这样回滚的时候只要把这个主键值对应的记录删除就可以了。
MySQL又一个鼎鼎大名的日志—— undo日志 ,正式登场!
undo log(撤销日志或回滚日志)记录了事务发生之前的数据状态,分为insert undo log和update undo log。
如果修改数据时出现异常,可以用 undo log来实现回滚操作(保持原子性)。可以理解为undo日志记录的是反向的操作,比如INSERT操作会记录DELETE,UPDATE会记录UPDATE之前的值,和redo日志记录在哪个物理页面做了什么操作不同,所以这是一种逻辑格式的日志。
undo日志和redo日志与事务密切相关,被统称为「事务日志」。
关于undo日志,我们目前只需要了解这么多即可
有了事务日志之后,我们来简单总结一下更新操作的流程,这是一个简化的过程。
name 原值是chanmufeng
。
update t_user_innodb set name ='chanmufeng1994' where id = 1;
之前我们讲过,从MySQL整体架构来看,其实可以分成两部分
redo日志是InnoDB存储引擎特有的日志,而Server层也有自己的日志,称为 binlog(归档日志),它可以被所有存储引擎使用。
我想你可能会问出这个问题,实际上,更准确的问法是为什么有了binlog还需要有redo日志?主要有以下几个原因。
binlog是实现MySQL主从复制功能的核心组件。
master节点会将所有的写操作记录到binlog中,slave节点会有专门的I/O线程读取master节点的binlog,将写操作同步到当前所在的slave节点。
假如你在阅读这篇文章的时候觉得我写得实在太好,拍案叫绝的时候一不小心把公司的数据库给删了,你该怎么做才能恢复到你删库之前的那个时刻的状态?
这个时候就要用到binlog了,前提是binlog没有被删除,否则,神仙也救不了你了。
通常情况下,公司会定期对数据库进行全量备份,可能隔一个月,一周,甚至可能每天都备份一次。运气好的话你可以使用前一天的全量备份,恢复到前一天的某时刻状态(或者一周、一月之前),然后从全量备份的时刻开始,从binlog中提取该时刻之后(前提是你的binlog里面存放了这段时间的日志)的所有写操作(当然,你得过滤掉你的删库操作),然后进行操作回放就可以了。
是不是很简单?
问题又来了。再看一眼我们的更新语句。
update t_user_innodb set name ='chanmufeng1994' where id = 1;
假如这条更新语句已经被写入到了redo日志,还没来得及写binlog的时候,MySQL宕机重启了,我们看一下会发生什么。
因为redo日志可以在重启的时候用于恢复数据,所以写入磁盘的是chanmufeng1994。但是binlog里面没有记录这个逻辑日志,所以这时候用binlog去恢复数据或者同步到从库,就会出现数据不一致的情况。
所以在写两个日志的情况下,就类似于「分布式事务」的情况,如果你不清楚分布式事务是个什么东西也没关系,我在之后的文章会介绍到。能够明确的就是redo日志和binlog日志如果单纯依次进行提交是无法保证两种日志都写成功或者都写失败的。
我们需要「两阶段提交」。
两阶段提交不是MySQL的专利,两阶段提交是一种跨系统维持数据逻辑一致性的常见方案,尤其在分布式事务上,所以请读者重点体会思想
我们把redo日志的提交分成两步,两步中redo日志的状态分别是prepare
和commit
。步骤如下
prepare
状态;commit
状态。至此,所有操作完成。加上两阶段提交之后我们再来看一下SQL更新语句的执行流程。
prepare
状态;commit
状态。完!
全部0条评论
快来发表一下你的评论吧 !