(十一)MySQL日志篇之undo-log、redo-log、bin-log.....傻傻分不清!

 任何项目都会有日志,MySQL也不例外,而且MySQL更是其中的佼佼者,日志种类繁多,而本篇的目的就是全解MySQL中的各类日志,如撤销日志、错误日志、慢查询日志、中继日志、回滚日志.....

   其实日志的作用不言而喻,无论是线上排查,亦或是性能优化,几乎都需要从日志中来获得信息作为依据,而MySQL中,很多很多的功能也都需要基于日志实现,比如事务回滚、数据持久化、数据恢复、数据迁移、MVCC机制.....,因此本篇去阐述日志,也是为了方便撰写后续的其他文章。

OK,废话不多说了,咱们现在就开始吧~

一、Undo-log撤销日志

   Undo即撤销的意思,但咱们通常也习惯称它为回滚日志,在日常开发过程中,如果代码敲错了,一般会习惯性的按下Ctrl+Z撤销,而Undo-log的作用也是如此,但它是用来给MySQL撤销SQL操作的。

在之前的《SQL执行篇》中曾聊到过,当一条写入类型的SQL执行时,都会记录Undo-log日志,会生成相应的反SQL放入到Undo-log中,例如:

  • 如果目前是insert插入操作,则生成一个对应的delete操作。
  • 如果目前是delete删除操作,InnoDB中会修改隐藏字段deleted_bit=1,则生成改为0的语句。
  • 如果目前的update修改操作,比如将姓名从竹子改成了熊猫,那就生成一个从熊猫改回竹子的操作。

当事务中某条SQL执行失败时,MySQL就需要回滚事务中其他执行成功的SQL,此时就会找到这个事务在Undo-log中生成的反SQL,然后将库中的数据改回事务发生前的样子。

大家看这段话,似乎没有啥问题对不?也包括我之前的文章中也是这样去描述的,但这里其实存在些许误导,在之前的《SQL执行篇》中,我们说一条写SQL执行前,会生成对应的反SQL记录在Undo-log中,但实际上并不会生成反SQL,这样去叙述仅是为了方便大家理解罢了。

那咱们怎么证明不会生成反SQL呢?如果大家有仔细研究过MySQL的日志,应该会发现Undo-log并不存在单独的日志文件,也就是磁盘中并不会存在xx-undo.log这类的文件,那Undo-log存在哪儿呢?InnoDB默认是将Undo-log存储在xx.ibdata共享表数据文件当中,默认采用段的形式存储。

也就是当一个事务尝试写某行表数据时,首先会将旧数据拷贝到xx.ibdata文件中,将表中行数据的隐藏字段:roll_ptr回滚指针会指向xx.ibdata文件中的旧数据,然后再写表上的数据。

Undo-log究竟在xx.ibdata文件中怎么存储呢?在共享表数据文件中,有一块区域名为Rollback Segment回滚段,每个回滚段中有1024Undo-log Segment,每个Undo段可存储一条旧数据,而执行写SQL时,Undo-log就是写入到这些段中。

不过在MySQL5.5版本前,默认只有一个Rollback Segment,而在MySQL5.5版本后,默认有128个回滚段,即支持128*1024Undo记录同时存在。

1.1、对于事务回滚原理的纠正

结合上述纠正后的内容,咱们再对事务的回滚原理稍作更正,实际上当一个事务需要回滚时,本质上并不会以执行反SQL的模式还原数据,而是直接将roll_ptr回滚指针指向的Undo记录,从xx.ibdata共享表数据文件中拷贝到xx.ibd表数据文件,覆盖掉原本改动过的数据。

还是上个图简单理解一下吧,如下:


一条写SQL执行的流程如上图中的序号所示,当需要回滚事务时,直接用Undo旧记录覆盖表中修改过的新记录即可!

如果是insert操作,由于插入之前这条数据都不存在,那么就不会产生Undo记录,此时回滚时如何删除这条记录呢?因为插入操作不会产生Undo旧记录,因此隐藏字段中的roll_ptr=null,因此直接用null覆盖插入的新记录即可,这样也就实现了删除数据的效果~

1.2、基于Undo版本链实现MVCC

认真阅读过《MySQL-MVCC机制原理剖析》的小伙伴对于这点并不陌生,Undo-log中记录的旧数据并不仅仅只有一条,一条相同的行数据可能存在多条不同版本的Undo记录,内部会通过roll_ptr回滚指针,组成一个单向链表,而这个链表则被称之为Undo版本链,案例如下:

-- 事务T1:trx_id=1(两次修改同一条数据)
UPDATE `zz_users` SET user_name = "竹子" WHERE user_id = 1;
UPDATE `zz_users` SET user_sex = "男" WHERE user_id = 1;
复制代码

Undo-log中的旧数据版本链示意图大致如下:


当然,对于如何利用版本链实现MVCC机制,这点就不反复赘述了,没了解过的可以去看关于MVCC原理剖析的那篇文章。

1.3、Undo-log的内存缓冲区

InnoDBMySQL启动时,会在内存中构建一个BufferPool,而这个缓冲池主要存放两类东西,一类是数据相关的缓冲,如索引、锁、表数据等,另一类则是各种日志的缓冲,如Undo、Bin、Redo....等日志。

而当一条写SQL执行时,不会直接去往磁盘中的xx.ibdata文件写数据,而是会写在undo_log_buffer缓冲区中,因为工作线程直接去写磁盘太影响效率了,写进缓冲区后会由后台线程去刷写磁盘。

OK~,那么如果当一个事务提交时,Undo的旧记录会不会立马被删除呢?因为事务都提交了,不需要再回滚改动过的数据,似乎用不上Undo旧记录了,对吗?确实如此,但不会立马删除Undo记录,对于旧记录的删除工作,InnoDB中会有专门的purger线程负责,purger线程内部会维护一个ReadView,它会以此作为判断依据,来决定何时移除Undo记录。

为什么不是事务提交后立马删除Undo记录呢?因为可能会有其他事务在通过快照,读Undo版本链中的旧数据,直接移除可能会导致其他事务读不到数据,因此删除的工作就交给了purger线程。

1.4、Undo-log相关的参数

最后再来看看关于Undo-log的一些参数,其实在MySQL5.5之前没有太多参数,如下:

  • innodb_max_undo_log_size:本地磁盘文件中,Undo-log的最大值,默认1GB
  • innodb_rollback_segments:指定回滚段的数量,默认为1个。

除开上述两个参数外,其他参数基本上是在MySQL5.6才有的,如下:

  • innodb_undo_directory:指定Undo-log的存放目录,默认放在.ibdata文件中。
  • innodb_undo_logs:指定回滚段的数量,默认为128个,也就是之前的innodb_rollback_segments
  • innodb_undo_tablespaces:指定Undo-log分成几
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值