0.前言
续上一篇: MySQL的7种日志(一):概况
上一篇我准备写MySQL日志还是2个月前的事,这两个月里生活发生了天翻地覆的变化,都没时间更新。
昨天跟朋友聊天立了flag说今天要续写一篇
于是中午吃饭时在纸上画了一个流程图。来介绍下MySQL里的RedoLog
1.几个问题
redolog和binlog一样记录的是数据修改后的记录。区别是什么,存在的意义是什么?
- 如果不要redolog,直接修改数据行不行? 答: 可以,但是随机读写性能差
- 先写redolog还是先改数据?答:先写内存里的数据,再写redolog,再写binlog,再写磁盘里的数据
- 先写redolog还是先写binlog? 答:先写redolog,再写binlog
- 如果写完redolog,还没来得写binlog时就停电了,怎么办?答:修改不要了,从undolog中回滚数据
- 如果写完redolog,binlog时还没来得数据落盘就停电了,怎么办?答:重做redolog,提交数据。修改有效
redolog和undolog的关系
- 答:redolog用来恢复丢失数据(恢复到最后一次提交位置)也称之为前滚操作,undolog是用来回滚到之前的版本,称之为回滚操作
relaylog的作用
- 答:redolog是用来做崩溃恢复使用的,这种崩溃恢复不需要我们人为的参与,MySQL自己内部自己实现了这种崩溃恢复的功能,我们只管享受这种功能给我们带来的服务即可,这种服务给我们的感受就是:MySQL数据库异常宕机的时候,重启服务之后,数据库中之前提交的记录都不会丢失数据仍然可以正常恢复,不管这种提交的记录是否已经更到具体的表所对应的磁盘page也中。
2.修改数据的流程
- 当我们要更新一条数据时,比如有一条SQL
update userinfo set name='dboop' where name='张三';
- 最直接的方法:从磁盘上找到对应的数据库文件,把它修改完存放到磁盘中。
- 方法是可以的,很多简单的程序修改文件也是用的方法,但是性能差。
- 而数据库中一般会有以下几种方式来写入数据修改
- 按页组织数据,一些关联近的数据存放在一个页中,MySQL中默认一页是16k
- 读取和修改数据都是需要先把页加载到内存中,MySQL是放到innodb_buffer_pool中
- 先改内存,再合适的时候再写入磁盘
- 先改日志再改数据
- 日志也是先写内存中的日志buffer,再合适的时候刷入磁盘
下图是简化版的一个数据修改,真实的流程比这复杂很多,这里的数据修改不只是update,按页组织的insert/update/delete操作都是对页修改
3.Redolog在数据库意外崩溃时的作用
当故障发生时,数据库意外当机,有部分内存中已修改的页(脏页)没来得及刷新到磁盘里。
在写入redo log时,会顺便记录XID,即当前事务id。在写入binlog时,也会写入XID。
如果在写入redo log之前崩溃,那么此时redo log与binlog中都没有,是一致的情况,崩溃也无所谓。
如果在写入redo log prepare阶段后立马崩溃,之后会在崩恢复时,由于redo log没有被标记为commit。于是拿着redo log中的XID去binlog中查找,此时肯定是找不到的,那么执行回滚操作。
如果在写入binlog后立马崩溃,在恢复时,由redo log中的XID可以找到对应的binlog,这个时候直接提交即可。
总的来说,在崩溃恢复后,只要redo log不是处于commit阶段,那么就拿着redo log中的XID去binlog中寻找,找得到就提交,否则就回滚。
在这样的机制下,两阶段提交能在崩溃恢复时,能够对提交中断的事务进行补偿,来确保redo log与binlog的数据一致性
4.Redolog的刷盘
4.1 redlog的分组
一个事务中可能会包含多个page的修改,可能会产生多条redolog,所以redolog以组的方式写入 在一个事物中多次的数据修改,对应的就是多个数据页多个偏移量位置的字段变更,也就是说会产生多条redolog,一个组的redolog在持久化的时候,不能部分成功,部分失败,否则的话,就会破坏事务的原子性。
4.2 redolog的块
redolog是按照块组织在一起,然后写入到磁盘中的,类似于数据的页,而且引入了redo log buffer,默认的大小为16MB。buffer中分了很多的block,每个block的大小为512kb,每一个事务产生的所有redo log称为一个group。
4.3 redolog 刷盘时机
redo log 在从内存中持久化到redo log file的时候,也是有策略的,会有相应的配置,来设置持久化的时机
innodb_flush_log_at_trx_commit,这个参数,就是控制redo log 从内存中,写入到磁盘中的时机
- 如果为0:提交事务时,不会强制将数据从内存写入到磁盘中,而是每隔1S,将数据从内存中,写入到os cache中,并通过fsync()命令刷入到磁盘中
- 如果为1:表示实时同步,在提交事务的时候,会将redo log buffer中的数据写入到os cache,并调用fsync()写入到磁盘中
- 如果为2:表示实时写,延迟刷;每次提交事务,都会将数据从redo log buffer写入到os cache中,但是会每隔1S,调用fsync(),将数据持久化到磁盘上
4.4 redolog刷盘流程简介
RedoLo是循环写的。write pos可以看作是头,CheckPoint可以看作是尾。w->c是空闲的区域,c-w是已经写满的区域。写入时挪动w,落盘时挪动c,当w=c的位置时需要等待去落盘。 checkpoint就是负责落盘的,将数据存储到磁盘上并擦掉,由一个异步线程来执行的。
5.Redolog的配置
InnoDb引擎至少有一个日志文件从做组,每个文件组至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile1。
innodb_log_file_size: 指定每个redo日志大小,默认值48MB
innodb_log_files_in_group: 指定日志文件组中redo日志文件数量,默认为2
innodb_log_group_home_dir: 指定日志文件组所在路劲,默认值./,指mysql的数据目录datadir
innodb_mirrored_log_groups: 指定日志镜像文件组的数量,默认为1,此功能属于未实现的功能,在5.6版本中废弃,在5.7版本中删除了。
show variables like ‘innodb%log%’; #列举RedoLog日志组的配置
总结.Redolog的作用
MySQL为了提升性能,每次对数据的更新,会先同步到BufferPool中,涉及到的数据页就会变成脏页。 同时会启动后台线程,异步地将脏页刷新到磁盘中。 如果在某个时间,MySQL突然崩溃,则内存中的BufferPool就会丢失,剩余未同步的数据就会直接消失。 虽然在更新BufferPool后,也写入了binlog中,但binlog并不具备crash-safe的能力。 因为崩溃可能发生在写binlog后,刷脏前。在主从同步的情况下,从节点会拿到多出来的一条binlog。 所以server层的binlog是不支持崩溃恢复的,只是支持误删数据恢复。InnoDB考虑到这一点,自己实现了redo log。
– done
>> Home