1、InnoDB的重要内存结构--缓存池
Innodb存储引擎中有一个非常重要对吧放在内存里的组件,就是缓存池(Buffer Pool),这里面会缓存很多的数据,以便于在后续查询的时候,如果这个数据已经在缓存池里面了,就可以不用去查询磁盘了,直接在内存中获取或者修改数据,避免频繁的IO。
2、如何让你更新的数据可以回滚--undo日志文件
如果我们更新了一条数据,如果这次更新是在一个事务里面的,那么这次更新在事务提交之前,我们是可以回滚这个事务的,也就是我们可以把数据在回滚回去,那这是怎么做到的呢?
Innodb存储引擎引入了一个undo日志文件,负责数据的回滚。在更新一条数据之前,会将原始数据先写入undo日志之中,如果我们要回滚事务,这需要从undo日志中拿出旧值覆盖即可。
image.png
3、万一系统宕机,如何避免数据丢失--Redo Log Buffer
我们知道,我们更新数据是在内存中的,这时候数据还没有进入磁盘之中,那么如果这时候我们的MySQL宕机了,内存中的数据比如会丢失,那怎么解决呢?
Innodb存储引擎为了解决这个问题,引入了一个新的内存结构Redo Log Buffer,在我们修改数据的时候,就必须把对内存所做的修改写入到Redo Log Buffer里面去,但是这个Redo Log Buffer还是在内存中的,如果这时候我们还没有提交事务,MySQL却突然宕机,必然会导致内存里Buffer Pool中的修改过的数据都丢失,同时写入Redo Log Buffer里面的数据肯定也会丢失,但是这种时候时候即使MySQL宕机数据丢失,也是没有关系的,因为我们的事务还没提交,也就是表示这个操作还没有执行成功,就算内存数据丢失了,磁盘数据也不会变化,你的执行也没有成功。 但是如果这时候事务已经提交了,我们的执行已经成功了,但是这时候MySQL却宕机了,那这时候肯定是不行的,毕竟我们都执行成功了,你不能说数据没了就没了。那怎么办呢?
那么我们可以想到,为了防止数据丢失,我们肯定要将数据持久化,也就是肯定要写入磁盘。 Innodb存储引擎确实也是这么做的,当我们想要提交一个事务的时候,会根据一个策略把redo日志从Redo Log Buffer里面写入到磁盘文件里面去,这样我们就可以保证即使MySQL宕机了,至少操作记录还是存在的,不会丢失,那么这个策略是什么呢?
这个策略通过innodb_flush_log_at_trx_commit参数来配置的,它有这么几个策略。 如果这个值为 0 ,那么提交事务的时候,不会把Redo Log Buffer里的数据刷入磁盘文件,此时如果事务已经提交了,MySQL却宕机了,那么你的数据妥妥的就没了,就可以考虑跑路了。 当这个值为 1 ,那么提交事务的时候,就必须把Redo Log Buffer从内存刷入到磁盘之中,只要事务提交成功了,那么就可以保证Redo Log一定已经在磁盘之中了,这时候就是MySQL宕机了,那么数据也不会丢。 当这个值为 2 ,那么提交事务的时候,会把Redo Log Buffer里面的数据写入磁盘对应的OS Cache里去,而不是直接进入磁盘,可能要1秒之后才会把OS Cache里面的数据写入到磁盘文件里面去,这时候就算MySQL宕机了,那么数据是不会丢失的,但是(凡是就怕但是),如果这时候连MySQL所在的这台机器也宕机了,那么数据还是妥妥的就丢了。
所以对应MySQL这种需要保持强一致性的引用,一定要保持这个参数的值为 1 ,保证不管是MySQL、还是机器挂了、至少数据不会丢失,也不用跑路。
4、Mysql binlog到底是什么
实际上我们上面说过的redo log,它是一种偏向物理性质的重做日志,因为它里面记录的是类似与这样的东西,“对哪个数据页中的什么记录,做了什么修改”;而且redo log本身是属于InnoDB存储引擎特有的一个东西。
而binlog叫做归档日志,它里记录的是偏向逻辑性的日志,类似与“对users表中的id=10这一行数据做了更新操作,更新以后的值是什么”。binlog不是innodb存储引擎特有的日志文件,是属于mysql server自己的日志文件。
5、提交事务的时候,同时会写入binlog
在我们提交事务的时候,会把redo log日志刷入磁盘,然后同时还会把这次更新对应的binlog日志写入到磁盘里面去。
6、binlog日志的刷盘策略
既然redo log有不同的刷盘策略,那么binlog肯定也有,binlog的刷盘策略通过sync_binlog参数进行控制,它有两个取值,分别为0和1。
sync_binlog=0,这个是其默认值,此时会将binlog先写入os cache里面去,然后经过一段时间之后再将其写入到磁盘上面去。这时候如果机器宕机,那么数据就会丢失。
sysc_binlog=1,此时在提交事务的时候,会强制把binlog直接写入到磁盘文件里面去,这样提交事务之后,就算机器宕机,磁盘上面的binlog日志也是不会丢失的。
7.基于binlog和redo log完成事务的提交
当我们把binlog写入到磁盘文件之后,接着就会完成最终的事务提交,此时会把本次更新对应的binlog文件名称和这次更新的binlog日志在文件里的位置,都写入到redo log日志文件里面去,同时在redo log日志文件里写入一个commit标记,在做完这个事情之后,才算最终完成了事务的提交。
8、最后一步在redo日志中写入commit标记的意义是什么?
commit标记用来保持redo log日志与binlog日志一致。 例如:假设提交事务的时候,必须要完成上面的5、6、7三个步骤,才算是提交了事务,那么如果有步骤没有完成会怎么样呢? 加入我们完成了步骤5的时候,也就是redo log刚刷入磁盘之后,MySQL就宕机了,这个时候因为没有最终的commit标记在redo日志里,所以此次事务可以判定不成功,不会因为redo日志文件里有这次更新的日志,但是binlog日志文件里没有这次更新的日志,不会出现日志不一致的问题。
如果完成了步骤6,redo log日志和binlog日志全都落盘了, 此时MySQL宕机了,那么因为redo log文件里面没有commit标记,因此这次事务提交也是失败的。
必须是在redo loh中写入最终的事务commit标记,然后此时事务提交成功,而且redo log里有本次更新对应发日志,binlog里面也有这次更新对应的日志,redo log和binlog完全是一致的。
9、后台IO先随机将内存更新后的脏数据刷入磁盘
在日志成功提交之后,我们的数据还是存在于内存之中(Buffer Pool),按这些数据肯定是要落盘的,这样才不会丢失,那么这个数据什么进入磁盘里面呢?
MySQL里面有一个后台的IO线程,会在某个时间里,随机的把内存Buffer Pool中的修改后的脏数据给刷回的磁盘上的数据文件里面去。
就算在刷盘之前mysql崩了或者机器崩了,也没关系,因为重启之后,会根据redo日志恢复之前提交事务做过的修改更新到内存里面去,然后等适当时机,IO线程再把这个修改后的数刷入到磁盘里面去。
10、总结
大家通过一次更新数据的流程,就可以清晰地看到,InnoDB存储引擎主要就是包含了一些buffer pool、redo log buffer等内存里的缓存数据,同时还包含了一些undo日志文件,redo日志文件等东西,同时mysql server自己还有binlog日志文件。
在你执行更新的时候,每条SQL语句,都会对应修改buffer pool里的缓存数据、写undo日志、写redo log buffer几个步骤;
但是当你提交事务的时候,一定会把redo log刷入磁盘,binlog刷入磁盘,完成redo log中的事务commit标记;最后后台的IO线程会随机的把buffer pool里的脏数据刷入磁盘里去。