mysql随笔二

注本文是本人在学习Mysql的过程中记忆巩固,参考许多资料并非个人原创,下面将列出本人参考资料:

小林coding 图解Mysql

《Mysql是怎样运行的:从根上理解Mysql》

mysql的日志格式

mysql的三种日志:nudoLog,redoLog,binLog

三种日志各司其职,缺一不可,需要注意的是binLog日志是mysql自带的所有执行引擎都可以使用,而nudoLog和redoLog是innoDB引擎自身实现。

nudoLog日志主要作用是实现事务的原子性(回滚)和实现了MVCC机制。

redoLog日志主要作用是保证事务的持久性。

binLog的主要作用是防止误删和归档,如果要配置mysql主从也需要binLog日志来保证主库与从库之间的数据一致。

nudoLog

nudoLog日志主要分为两种类型:一种添加类型,一种为修改类型(删除或者修改),nuduLog有专属的数据页有时一个数据页可能放不下所以需要使用链表来将所使用的nudoLog,且nudoLog页中只能存放一种数据类型,所以一个十五如果同时涉及增和改他将会拥有两个nudoLog链表,所以一种事务提交可能最多需要四个链表(临时表生成的nudoLog页需要单独保存)

添加语句生成的nudoLog页因为不需要参加MVCC机制,所以在事务提交之后添加添加nudoLog页就可直接释放或者直接重用。

MVCC机制与行格式有关,InnoDB的行格式会有两个或三个隐藏列(若无主键隐藏的造一个主键),比较重要的是trx_id 是本次修改本行的事务id,roll_pointer 则指向上一次修改的nudoLog日志地址,而上次修改的nudoLog日志也会保存trx_id 与roll_pointer 多个nudoLog日志连接起来就形成了版本链,来构成MVCC机制。

有意思的是在事务中删除一行时并不会马上删除,而是会在删除行的打上删除标记,在事务提交之后在将该行删除并放到垃圾链表中,再向一个表中添加数据时,会检查垃圾链表的头结点若垃圾链表的头节点可容纳改行数据则直接使用头节点空间,还有个问题若数据小于头节点所占空间则会产生碎片,mysql会在合适的时机处理该页将该页数据复制到一个新页上然后将页上所有数据重新插入一遍。

nudoLog页也需要依据redoLog进行持久化到磁盘

redoLog

redoLog日志来保证事务在执行之后不会持久化的问题,好端端的为啥事务提交之后不会持久化到磁盘上呢?好问题,这就要引用到innoDB使用的WAL机制,简单来说就是先写日志后写磁盘,可能这里还有点疑惑我们接着往下说,mysql修改一句的流程,首先需要检查这页是不是已经在内存当中了,如果有则直接在内存上修改,如果没有则需要先把这页读到内存当中,然后再去修改,修改完成之后正常情况来说在事务提交之后应该直接持久化到磁盘当中,但是有一个问题我修改一下就需要读一下然后再持久化一下,IO占用就比较高,所以就引入了buffer bool,之后就引入了WAL机制与redoLog日志,我们先来说WAL机制具体是怎么回事,我们在一个事务当中在修改某一行时,我们修改内存之后在写redoLog日志事务就算完成了,我修改了内存页有没有刷到磁盘上什么时候刷到磁盘上都不关这个事务的事情了,这个事务就已经算是完成,mysql会使用后台进程把脏页刷到磁盘上,buffer bool这个也很重要一会我们仔细说。

然后我们来说redoLog日志,首先我们修改某些行之后我们首先会把redoLog保存到redo log buffer根据mysql的机制来决定什么时候持久化到磁盘上,我们来说一下redo log buffer的LSN,当中保有两个LSN一个最后保存在redo log buffer当中的事务的LSN,LSN之后都是redo log buffer空余空间,还有一个LSN是已经刷新到磁盘上的LSN,此LSN之前的都是已经刷新到磁盘上的,磁盘上redoLog日志会保有两个LSN一个为最后事务的LSN,该LSN之后的都为空余空间,还有一个LSN标识之前的LSN都已经刷新到磁盘上,也就是表示这些脏页都已经刷新磁盘上了。

edoLog日志是一组文件,从头写到尾,写完一这一个写下一个,而且是循环写,例如文件都写完了就会从第一个文件重新从头开始,因为redoLog日志中写的部分脏页已经被刷新到磁盘上了,既然已经刷新到磁盘上了这些日志也就没有保留的必要了。

我们来说一下redoLog日志的恢复过程,首先我们需要找到最后的确定起点和终点,其实它们都在redoLog当中已经写好了,之前我们说过有两个LSN一个来表示之前的都已经刷新到磁盘上了,刷新到磁盘上自然就不用恢复了,所以我们的起点就确定好,我们再来说终点,其实也很简单直接到最后的LSN中即可,然后我们需要考虑一种情况LSN中有部分脏页可能已经刷新到磁盘上了,这个在页上会有标记,页上会标记最后一次修改页的LSN,若大于我们起点的LSN则表示该页已经刷新到磁盘上直接跳过即可,解决这些问题之后呢,mysql会把索引的起点LSN之后的redoLog日志读出来,然后按照表空姐和页号为Key页为value构建Hash表,重复的表则使用链表按照修改顺序先后相连,这样做的好处呢就算变随机IO为顺序IO还避免了回复顺序错误导致数据不一致的情况。

我们如何知道哪些脏页已经被刷新到磁盘上了哪些还没有?很好的问题,这时候我们又要请到我们的buffer bool了,buffer bool当中会保有一个脏页链表,当中的每页会保有两个字段,一个字段为事务开始时的LSN,另一个字段为事务结束时的LSN值,mysql后台会从链表的尾部刷新到磁盘,所以链表最后脏页的事务开始的LSN就有作用了,只要小于这个LSN就表示该页已经被刷新到磁盘上了;有一个点需要特别注意一下,如果在脏页为刷新到磁盘前又修改了本页那么只会修改本页中事务结束的LSN,还会把该页移动到链表头部。

binLog日志

binLog日志主要作用是防止误删和归档,还在mysql主从同步时发挥着非常重要的作用,且binLog日志记录的记录的是全量日志,一个日志记录完成,然后会继续继续记录下一个日志,不会覆盖之前的日志。

bingLog记录与redoLog记录类似,都是先存贮在内存当中,在事务提交时根据mysql具体配置来决定什么时候持久化到磁盘当中。

我们来说一下双重提交机制,为什么会有双重提交机制?因为mysql从库依赖binLog日志进行数据同步,但是主库会跟据redoLog恢复事务,所以二者必需同时成功或者失败,否则将会出现主库从库数据不一种的情况(双1策略)。然后我们来说怎么实现的双重提交机制,首先会先写redoLog日志和XID并将redoLog状态设为prepare 然后去写binLog,日志也会将XID写到binLog日志中,binLog日志写入完成后会将redoLog状态设为commit ,执行然后就表示改事务执行成功。发生崩溃的几种状态,若是在redoLog为prepare状态则需要分情况进行,若是此时binLog也写入完成则表示事务已经完成,提交即可,若binLog中数据不完整则表示事务事务没有执行完成要进行回滚,怎么判断binLog是否写入完成?判断redoLog中XID是否binLog也包含,若不包含则表示这些事务没有执行完成需要回滚。

binLog是全量数据为什么不能替代redoLog

历史原因:innoDB是mysql后期引入,mysql原生的执行引擎本来就没有考虑崩溃恢复的问题,redoLog是innoDB自己实现的崩溃恢复机制。

现实原因:binLog记录是逻辑数据,也就是哪个表的哪一行发生了什么变动,而redoLog记录的是物理数据,记录的是哪个页多少偏移量上发生了什么变动,而且mysql的事务提交是基于WAL机制的,也就是说事务写完日志改完内存就表示事务已经结束了,是不管内存页刷新到磁盘的事情的,而binLog是无法恢复内存页的。说的再简单明了一点,binLog日志根本不知道执行事务那些页已经刷新到磁盘上了,所以它根本就恢复不了未刷新到磁盘上的脏页。

buffer bool

buffer bool是mysql中很重要的一部分,mysql修改数据需要将数据页先读到内存页中,然后在内存中修改,实际上在buffer bool中缓存页有一个相对应的控制块,控制块与缓存页一一对应,控制块是为了更好的管理这些缓存页。

当我们需要在buffer bool取出一个空的缓存页时我们毕竟不能傻傻的去遍历整个buffer bool所以我们引入了一个链表,这个链表中只存缓存页为空的所对应的控制块,若是我们需要一个空的缓存页我们直接去链表里去一个出来就好。

当我们要在内存中修改某些页时,这些页将会被标记为脏,这些脏页也会被串成一个链表,mysql会在合适的时机将将一部分脏页刷新到磁盘当中。

buffer bool是内存,内存是宝贵的我们想要尽量在当中保存重要的数据,这时候引入的LRU链表,如果读一个页就把改页放在链表的头部,链表的尾部就是比较不常用的数据,而头部就是比较常用的数据,但是这样的LRU算法解决不了mysql的预读问题,简单来说就是就是mysql会把你可能会用到的页面提前读到buffer bool中(线性预读,随机预读),这本来是好事,但是预读会读取大量数据可能会导致LRU链表中的热门数据反而被挤出链表,所以引入将LRU链表分为两部分,尾部一部分为冷,头部为热,预读的数据首先要放到尾部的冷数据那,等到改缓存页真的被读取到了再将改缓存页防止到热的部分,但是这仍然解决另一个问题,那就是全表扫描,如果执行全表扫描的话可能会对某些页短时间内多次访问,要解决这个问题,要有一个前提,全表扫描不会经常执行,当然谁页不会闲着没事全表扫描玩,所以引入了如果这个页面的第一次访问和最后一次访问若不超过某个限制则将改页仍然放在冷的部分,这个限制是可以通过设置调整,默认为1秒。

buffer bool空间终究是有限的,若真是到了一点空间都没有了,mysql会首先从LRU链表尾部查看是否有不使用的缓存页,有则直接释放,若无则被迫同步的将脏链表中的部分页刷新到磁盘,我们都知道磁盘相对于内存是很慢的,所以这样比较慢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值