(1)redo log 和 binlog日志:
redo log 是一种偏向物理性质的重做日志,它里面记录的是类似 “对哪个数据页中的什么记录,做个什么修改”,redo log本身属于 InnoDB存储引擎特有的。
binlog 叫做归档日志,记录的是偏向于逻辑性的日志,类似于“对users表中的id=10的一行数据做了更新操作,更新以后的值是什么”。另外binlog不是InnoDB存储引擎特有的日志文件是属于Mysql Server层面的日志文件,每个存储引擎都会有。
(2) 提交事务的时候,同时会写入binlog:
在我们提交事务的时候,会把redo log日志写入磁盘文件中去,其实在提交事务时,我们还会把这次更新对应的binlog日志写入到磁盘文件中去。 执行器跟InnoDB存储引擎进行交互步骤:
(1)从磁盘里加载数据到Buffer Pool中进行缓存
(2)写入undo日志文件
(3)更新Buffer pool里的数据
(4)写入redo log buffer
(5)redo log刷入磁盘
(6)写binlog
(7)写入binlog文件名称和更新的binlog日志在文件中的位置 commit标记
实际上,执行器是一个非常核心的一个组件,负责跟存储引擎配合完成一个SQL语句在磁盘与内存层面的全部数据更新操作,一个更新语句的执行可以分为两个阶段:前4个步骤是执行这个更新语句干的事,5和6两个步骤,是开始提交事务时候做的事,属于提交事务阶段。
(3)binlog日志的刷盘策略分析:
binlog日志也有不同的刷盘策略:
通过sync_binlog参数控制,默认值为0,当你把binlog写入磁盘的时候,其实不是直接进入磁盘文件,而是进入os cache内存缓存,如果机器宕机,os cache里的binlog日志是会丢失的;
如果sync_binlog参数设置为1,那么会强制在提交事务的时候,把binlog直接写入磁盘文件里去,这样提交事务之后,即使机器宕机,磁盘上的binlog是不会丢失的。
(4)基于binlog 和 redo log完成事务的提交:
当我们把binlog写入磁盘文件之后,接着就去完成最终的事务提交,把本次更新的 binlog文件名称和这次更新的binlog日志在文件的位置,都写入到redo log日志文件里去,同时在redo log日志文件里写入一个commit标记,在完成了这个事情之后,才算最终完成了事务的提交。
(redolog是物理性质的日志文件,redolog存储binlog日志也是基于物理性能,即在哪个binlog文件修改了哪个位置的数据)
(5)最后一步在redo日志中写入commit标记的意义是什么?
其实是用来保持redo log日志与binlog日志一致的,例如:我们在提交事务的时候,一共有5,6,7步骤(redo log刷入磁盘,binlog 刷入磁盘,binlog文件名及binlog日志位置写入redo log日志文件),必须是三个步骤都执行完毕,才算提交了事务。
那么在我们刚完成5步骤的时候,也就是redo log刚刷入磁盘文件的时候,mysql宕机了,此时怎么办?这个时候因为没有最终的事务提交标记(commit) 在redo日志里,所以此次事务可以判定为失败。不会说redo日志文件里有这次更新的日志,但是binlog日志文件里没有这次更新的日志,不会出现数据不一致的问题。
如果要是完成步骤6的时候,也就是binlog写入磁盘了,此时mysql宕机了,怎么办?同理,因为没有redo log中的最终标记,因此事务提交也是失败的,必须是在redo log中写入最终的commit标记,然后此时事务提交成功,而且redo log里有本次更新对应的日志,binlog里也有本次更新对应的日志,redo log和binlog完全是一致的,才算成功。
(6)后台IO线程随机将内存更新后的脏数据刷回磁盘:
假设已经提交事务了,此时一次更新 “update users set name=’xxx’ where id=10 ”, 他已经把内存里的buffer pool中的缓存数据更新了,同时磁盘里有redo日志和binlog日志,都记录了我们指定的”id=10”这行数据的修改了“name=’xxx’”, 此时,磁盘的数据文件里”id=10”这行数据的name字段还是等于 zhangsan 这个旧值的。
所以MySQL有一个后台的IO线程,会在之后某个时间里,随机的把内存buffer pool中的修改后的脏数据刷到磁盘的数据文件里去。当IO线程把buffer pool里的修改后的脏数据刷到磁盘后,磁盘上的数据才会跟内存里一样,都是“name=’xxx’”这个修改后的值,在你IO线程把脏数据刷到磁盘前,哪怕MySQL宕机崩溃也没关系,因为重启之后,会根据redo日志恢复 之前提交事务做过的修改 到内存里去,就是id=10的数据name修改成了xxx,然后等适当时机,IO线程把这个修改后的数据刷到磁盘文件里。
(7)基于更新数据的流程,总结一下InnoDB存储引擎的架构原理:
通过一次更新数据的流程,就可以清晰看到,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里的脏数据刷入磁盘文件里。
(8)执行更新操作的时候,为什么不直接修改磁盘上的数据?
磁盘文件的随机读写性能是很低的,更新属于随机写,undolog,redo log,还是binlog都是顺序写,所以性能高。磁盘顺序写的性能其实是很高的,某种程度上说,几乎可以跟内存随机读写的性能差不多,尤其是在数据库里其实也用了os cache机制,就是redo log顺序写磁盘之前,先进入os cache,就是操作系统管理的内存缓存单就保证提交事务后,数据一定会在磁盘更新来说,只要redo log日志文件就可以了,binlog是用来同步数据的。
问题1:IO线程没有将数据刷入磁盘前,mysql宕机重启之后是如何判断在redo log中哪些数据是需要恢复的?
IO线程每次刷回磁盘后,是知道本次刷新对应了哪些事务修改的,如果一些事务的修改没刷新回磁盘,他是知道的,然后把事务的redo log读取出来恢复就可以了。
问题2:如果脏页很多数据没刷新到磁盘,此时Mysql挂了,这时MySQL是怎么处理的?
假设redo log还没刷,data page刷入磁盘了,结果MySQL挂了,事务提交失败,用undo log把之前刷入磁盘的data page加载到cache page然后回滚修改就行了; msqyl宕机已经刷入磁盘的数据需要进行回滚,如果事务没提交,且redolog还没刷入磁盘,那就只能用undolog日志文件把之前刷入磁盘的数据加载缓存页然后修改回来就行了。(事务是否提交,如果事务提交失败,使用undo log日志进行事务回滚刷新把数据加载到缓冲区重新操作,如果事务提交成功了,但是IO线程还没把数据刷入磁盘,这时宕机,这时需要根据redo log日志文件将数据加载到buffer pool中重做日志就可以了)
问题3:IO线程是如何识别buffer pool里哪些数据是事务提交成功?会不会把哪些没有提交事务的数据输入磁盘?
用链表来维护被修改的缓存页redolog日志文件被设计成可循环使用的,当日志文件写满时,redolog日志中对应数据已经刷新到磁盘的那部分不再需要的日志可以被覆盖重用。buffer pool缓冲区的数据页根据id会组成链表,比如free链表,flush链表等,这个IO线程会把flush链表对应的已提交事务的数据输入磁盘。