浅析Innodb crash recovery

         今天想谈谈Innodb crash recovery是因为工作中遇到的两个问题涉及到了它。实际上在何登成的博客也从源码角度分析这个过程,但是个人感觉一般人难得理解,事实上一般的DBA也没有必要做那么深入的研究。言归正传,当初工作中遇到的问题是什么呢?第一,一个同事执行一个大表操作,结果就很长时间没执行完,然后kill掉这个实例(这种做法不好),然后再重启实例时后mysql连上去,发现无法对刚才那个表做操作,hang住了。然后查看错误日志惊奇的发现innodb竟然还在进行回滚(roll back)。第二个问题,一个同事执行了一个4kw行的load data infile操作,结果执行了一晚还是完,于是kill掉这个操作,重启后也是发现对这个表无法做操作,show process list发现停留在check permission阶段(mysql版本5.5)。

        这两个问题都是由于一个超大事务引起的。对于第一个问题,delete操作没错,引起我注意的是,在crash recovery没有完全完成以前竟然新的连接可以连上去。之前一直认为当客户端可以连上实例的时候,那么就表示crash recovery完成。为此去手册上查看了相关章节The Innodb recovery process找到了答案,事实上就是这样的,我们之前没遇到过这种现象是因为事务很小,一瞬间就完成,因此忽略了这一点。首先上的原话是这样的:

a>InnoDB crash recovery consists of several steps. The first step, redo log application, is performed during the initialization, before accepting any connections。

这句话说明在redo过程中任何客户端的连接都是进不来的,会阻塞。

b>The remaining steps after redo log application do not depend on the redo log (other than for logging the writes) and are performed inparallel with normal processing. These include:1.Rolling back incomplete transactions. 2. Insert buffer merg. 3. Purge

这句话说明redo后面的恢复过程(回滚、合并insert buffer、purge undo)这个是在后台执行的,这个过程客户端就可以连上mysql实例进行操作了。

那么它这么做的好处在哪呢?在后台进行回滚的过程中,与回滚操作无关的表可以正常工作这个可能是当初这个设计的一个原因吧,当然回滚相关的表自然就无法操作,上面的例子就说明了这一点。

        对于第二个问题,load data infile导入很大的文件,这里主要分析两点:1.为什么当初导入很慢?有什么比较高效的方法?2.load data infile是不是一个事务?如果是一个事务,那么这个事务产生的redo日志超过了redo log设定的大小该怎么办?

        load data infile导入很慢,一个晚上也没完成,起初我怀疑是这样的:load data infile可能是一个事务,那么这个事务日志可能会超过设定的redo文件大小,而一个事务不能覆盖自己的事务日志,因此导致操作卡住。在这里简单的提及它 事实不是这样的,一个事务可以覆盖自己的事务日志(load data infile是一个日志在前面那篇文件已证明),在事务进行过程中如果发现redo log文件写满就会开始覆盖前面部分,而在覆盖前面的部分必须将这部分事务日志所涉及的脏页都刷到磁盘上,这个流程跟正常的一模一样。那么你可能会问假如这个事务失败了怎么办?那不会导致事务的一部分写到磁盘上造成数据不一致吗?答案是不会,因为对于那部分已经写入到物理磁盘上的部分,innodb会根据undo进行恢复,没有写入到磁盘上的部分自然就不用管它了。关于这部分具体怎么进行的,自己还没理得大清,到时候再请公司大牛帮解释解释,然后分享出来。再回到前面,load data  infile肯定不是由于事务日志大小超过设定的redo log大小造成的。其实造成它巨慢有两个原因:1.load data infile里面插入的顺序可能不是按照主键顺序,所以造成了大量的随机IO,2.被load的表有比较多的索引,那么在load过程中维护索引的代价太大,造成速度极低。因此解决方法可以从这三个方面入手:1.尽量使文件里面的行是按照主键排序,或者load之前加大buffer pool(不过遗憾这个不能动态设置)2.想方法在load之前不用索引,等load完后再添加。3.将load data infile人工控制,不在一个事务进行,中间进行提交,这可能不会对效率有太大提升,但是我个人认为对整个load操作成功率提升有比较大的帮助。

        标题是innodb crash recovery,却花了大部分篇幅讲其他,接下来谈谈crash recovery的大致流程:

1.从redo log文件里面得到最后一次checkpoint 发生的LSN
2.从这个点开始应用重做日志(前面说了这一步是阻塞客户端连接的) 

3.接下来就是进行undo,反做哪些未提交的事务(因为是先写日志方式,所以可能日志文件里面已经记录事务日志,但是最后事务却没提交成功,所以现在这个过程就是将这些事务取消)

4.在第3步的进行时又是分两种情况的:

a>如果开启了binlog,那么在恢复过程中判断哪些事务未提交时就会利用binlog(binlog一定是只记录提交事务。这里顺便提一句在mysql 5.5中正是由于为了保证binlog和redo log日志的顺序一致性,从而导致binlog group commit不能进行)。

b>如果没有开启binlog,那么就只能纯粹利用redo log了,事实上它会拿redo log entry的LSN与这行日志对应被修改的页的LSN进行比较,如果页的LSN大于等于redo log entry,那么就表示这个页是干净的,不需要被回滚掉。


最后如果我的理解哪里有错,欢迎指出,一起学习,一起进步!


你可能感兴趣的:(mysql,application,buffer,Crash,initialization,磁盘)