暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

你今天脱发了么|MySQL学习笔记-redo log

小肖爱吃肉 2019-08-19
297

MySQL在插入一条语句时,服务端将更新的数据写入内存数据页中就任务更新操作完成了,在合适的时候才会将数据刷入磁盘中,这样做的好处是提升了更新操作的效率。但存在的风险点就是系统发生异常崩溃的时候,这部分没来的及刷入磁盘中的数据就会丢失,产生数据不一致问题。InnoDB使用了redo log很好地解决了这个问题。


数据落盘

InnoDB用buffer pool作为数据库页面的缓存,InnoDB会将数据的修改操作放入缓存,记为“脏页”(当内存数据页跟磁盘数据页内容不一致时,称这个存储页为“脏页”)然后在合适的时候flush到磁盘,避免了每次修改数据都产生IO操作,降低更新操作的时长。问题:如果缓存中的数据还没有刷入磁盘系统就非正常关闭了,这些数据就会丢失,redo log的作用就是在系统重启后来修复这些数据

1.如果是正常运行的实例,数据页被修改后跟磁盘的数据页不一致,称为脏页。最终数据落盘,就是把内存中的数据页写入磁盘。

2.在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会将它读到内存,然后让redo log更新内存内容,更新完成后内存页变为脏页,就回到了第一种状态


flush过程

将内存中的脏页数据刷到磁盘中的过程称为flush

innodb_max_dirty_pages_pct:脏页的上限比例

innodb_io_capacity:告诉InnoDB所在主机的IOPS

innodb_flush_neighbors:是否开启连坐机制

flush的执行时机:

① InnoDB的redo log写满了,系统停止所有更新操作,把checkpoint往前推进,腾出redo log空间

② 系统内存不足时会淘汰一些数据页,如果淘汰的是“脏页”就要将脏页写到磁盘里

③ MySQL认为系统“空闲”的时候会刷脏页

④ MySQL正常关闭时会把内存中的脏页都flush到磁盘上


redo log基本概念

redo log是一种基于磁盘的数据结构,记录了对实际数据文件的物理变更,redo log记录的是物理页上的修改,redo log file中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录,具有幂等性。在故障恢复期间用于更正不完整事务写入的数据,具有carsh-safe功能(InnoDB保证数据库异常重启后,之前提交的记录都不会消失)。InnoDB采取了WAL(write-ahead logging,日志优先落盘),具体来说,当有一条记录需要更新的时候InnoDB引擎会先把记录写到redo log里并更新内存,这个时候更新就算完成了。也就说在实际数据文件的的修改落盘之前redo log就已经写入了磁盘从而来保证事务的持久性。主要的应用场景是用来进行MySQL的Crash recovey(奔溃恢复)。InnoDB将数据变更记录存入redo log中,然后将数据的变更写入内存中的缓存中。

write pos:当前记录的位置

checkpoint:当前要擦除的位置

redo log格式文件:storage\innobase\include\log0log.h log_t

默认情况下,redo log在磁盘上由两个名为ib_logfile0和ib_logfile1的物理文件表示。MySQL以循环方式写入重做日志文件,当全部文件写满后会回到第一个文件相应的位置进行覆盖写


查看redo log大小:SHOW VARIABLES LIKE ‘%innodb_log_file_size%’ (查询结果为48MB)

查看redo log分组 :SHOW VARIABLES LIKE ‘%innodb_log_files_in_group%’

在磁盘目录:……\MySQL-Servier-5.7\data下可以看到对应的存储文件:

MySQL的redo log大小 = innodb_log_file_size * innodb_log_files_in_group


更改redo log文件的数量或大小

1.停止MySQL服务器并确保它关闭(Windows:进入任务管理器确保关闭mysql进程)

2.编辑my.cnf(windows:my.ini)以更改日志文件配置

#单个日志文件大小

innodb_log_file_size = 48M

#文件个数

innodb_log_files_in_group = 3

3.如上命令,将文件个数改为3,再次启动MySQL服务器,在磁盘文件中会增加一个名为ib_logfile2的文件


redo log的生命周期

redo log record的生命周期

1.redo log record由mtr生成并保存在mtr的local buffer中记录数据库恢复阶段所需要的所有信息

2.调用mtr_commit给redo log record生成一个lsn用来确定这个记录在log file中的位置,redo log record被记录在redo log buffer中

3.redo log buffer会将record写到磁盘的redo log文件中

4.如果redo log record的lsn相关联的页面都写入到了磁盘,那么磁盘中redo log file对应的log record空间可以被循环利用

5.在崩溃恢复时用redo log来恢复数据


因为redo log buffer是线程共享的内存空间,所以有时候某个事务产生的redo log有可能在这个事务还没有提交的时候就已经被刷入到磁盘里了,没有提交的事务的redo log写入磁盘的情况如下:

① 后台线程每秒一次的轮询操作

② redo log buffer占用空间达到innodb_log_buffer_size一半时,后台线程主动写盘到page cache

③ 并行的事务提交时,将redo log buffer中其他事务的日志也一并持久化到磁盘中


innodb_flush_log_at_trx_commit参数

这个参数的作用是控制redo log刷入磁盘的时机,具体值如下:

0:事务commit时只把数据写入redo log buffer,每秒将日志从log buffer写入log file中并触发flush操作,速度最快,但不安全,MySQL进程崩溃会导致上一秒所有的事务数据丢失

1:系统默认值,事务commit的时候将redo log写入磁盘并调用fsync(),这种情况及时系统崩溃也不会丢失数据,确保了事务的ACID特性,但每次提交都有IO操作

2:事务在commit的时候将日志写入log file,每秒将日志flush到磁盘一次,系统崩溃/断电时会导致上一秒的数据丢失

InnoDB的后台线程会每隔一秒把redo log buffer中的日志写到page cache中,然后持久化到磁盘,但是如果遇到断电的这种情况,写入page cache的数据也会造成丢失

对于bin log,也有相应的参数可以设置刷磁盘频率。在主从结构中,为了保证主从事务的一致性,最好将参数设置为“双1”形式:

bin log:syn_binlog = 1

redo log:innodb_flush_log_at_trx_commit = 1

这两个配置保证了每次提交事务bin log和redo log都能及时刷到磁盘中


两阶段提交

两阶段提交示意图如下(参考:极客时间-MySQL45讲)

MySQL在执行commit命令时,会进行两阶段提交

1.将当前事务的状态设置为prepare状态

2.bin log提交函数sql.binlog.cc mysql_bin_log.commit(thd, true)

3.写redo log,这里就会对刷入时机进行判断

4.调用trx_commit_for_mysql函数


为了提升更新效率,MySQL设定了组提交机制,核心思想就是能让每次提交能带尽量多的日志写入到磁盘中,减少IO交互

组提交的参数设置:

binlog_group_commit_sync_delay:表示延迟多少微妙后才调用fsync

binlog_group_commit_sync_no_delay_count:表示累计多少次以后才调用fsync

这两个条件只要满足一个就会调用fsync


崩溃恢复

在数据库重启时,会对异常关闭时磁盘中产生的不一致的数据进行数据恢复(及时上次是正常关闭,MySQL在启动时也会进行崩溃恢复的流程来保证数据的一致性)

InnoDB在启动时会调用崩溃恢复的函数,入口storage\innobase\handler.ha_innodb.cc.innobase_start_or_create_for_mysql()

redo log 和bin log通过XID字段进行关联,在崩溃恢复的时候,会按顺序扫描redo log:

• 如果redo log又有prepare又有commit,直接提交

• 如果只有parepare,没有commit的redo log,用XID去找对应的bin log,验证bin log如果是完整的话,就提交,否则回滚事务

(判断bin log完成就提交:因为此时已经写入bin log了,这时bin log会同步到从库,为了保持主从的一致性,在主库也要提交这个事务)


有关redo log的优化准则

最后,MySQL官方文档给出了redo log的一些优化准则:

① 使redo log文件变大,甚至与缓冲池一样大 。如果 InnoDB将redo log文件写满,它必须将修改后的缓冲池内容写入检查点的磁盘 。小的redo log文件会导致许多不必要的磁盘写入

② 使用innodb_log_file_size 和 innodb_log_files_in_group 配置选项配置重做日志文件的大小和数量

③ 考虑增加日志缓冲区的大小 。大型日志缓冲区使大型事务能够运行,而无需在事务提交之前将日志写入磁盘。因此,如果有更新,插入或删除许多行的事务,则使日志缓冲区更大可以节省磁盘I O. 使用innodb_log_buffer_size 配置选项配置日志缓冲区大小 默认16M


参考文献

https://www.cnblogs.com/cheng2015/p/7685017.html my.ini配置文件

https://blog.51cto.com/wangwei007/2287431 redo log概念解析

https://blog.csdn.net/codepen/article/details/52160715 innodb_flush_log_at_trx_commit参数

https://www.cnblogs.com/liuhao/p/3714012.html redo Log log0log.h文件解析

https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html 两阶段提交解析

https://blog.csdn.net/weixin_33816946/article/details/85582540 两阶段提交源码解析

http://www.linkedkeeper.com/1500.html MySQL InnoDB 存储引擎大观

极客时间-MySQL45讲课程

MySQL5.7官方文档

 ———————————————— 

版权声明:本文为CSDN博主「没人搭理的二狗子」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_43188031/article/details/95750956


文章转载自小肖爱吃肉,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论