MySQL redo log和undo log 原理
MySQL的日志有很多种,如二进制日志(binlog)、错误日志、查询日志、慢查询日志等,此外InnoDB存储引擎还提供了两种日志:redo log(重做日志)和undo log(回滚日志)。这里将重点针对InnoDB引擎,对重做日志、回滚日志这三种进行分析。
innodb事务日志包括redo log和undo log。redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作。
undo log不是redo log的逆向过程,其实它们都算是用来恢复的日志:
- redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)
- undo用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。
redo log和二进制日志的区别
redo log不是二进制日志。虽然二进制日志中也记录了innodb表的很多操作,也能实现重做的功能,但是它们之间有很大区别。
- 二进制日志是在存储引擎的上层产生的,不管是什么存储引擎,对数据库进行了修改都会产生二进制日志。而redo log是innodb层产生的,只记录该存储引擎中表的修改。并且二进制日志先于redo log被记录。
- 二进制日志记录操作的方法是逻辑性的语句。即便它是基于行格式的记录方式,其本质也还是逻辑的SQL设置,如该行记录的每列的值是多少。而redo log是在物理格式上的日志,它记录的是数据库中每个页的修改。
- 二进制日志只在每次事务提交的时候一次性写入缓存中的日志"文件"(对于非事务表的操作,则是每次执行语句成功后就直接写入)。而redo log在数据准备修改前写入缓存中的redo log中,然后才对缓存中的数据执行修改操作;而且保证在发出事务提交指令时,先向缓存中的redo log写入日志,写入完成后才执行提交动作。
- 因为二进制日志只在提交的时候一次性写入,所以二进制日志中的记录方式和提交顺序有关,且一次提交对应一次记录。而redo log中是记录的物理页的修改,redo log文件中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。例如事务T1,可能在redo log中记录了 T1-1,T1-2,T1-3,T1共4个操作,其中 T1 表示最后提交时的日志记录,所以对应的数据页最终状态是 T1 对应的操作结果。而且redo log是并发写入的,不同事务之间的不同版本的记录会穿插写入到redo log文件中,例如可能redo log的记录方式如下: T1-1,T1-2,T2-1,T2-2,T2,T1-3,T1 。
- 事务日志记录的是物理页的情况,它具有幂等性,因此记录日志的方式极其简练。幂等性的意思是多次操作前后状态是一样的,例如新插入一行后又删除该行,前后状态没有变化。而二进制日志记录的是所有影响数据的操作,记录的内容较多。例如插入一行记录一次,删除该行又记录一次。
redo log的基本概念
redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。
image.png万一脏页还没来得及刷新到磁盘中,MySQL就挂了,怎么办呢?
对于业务代码来说,方才执行的事务是OK的,甚至前端都接受到了请求成功的响应。那结果修改的数据没同步回磁盘,MySQL宕机了会不会导致真实数据和逻辑上的数据不一致呢?
其实不会的!
MySQL使用redo log解决了这个问题,redo顾名思义:重做。
当发生事务(增、删、改)时会导致缓存页变成脏页,于此同时MySQL会将事务涉及到的:对 XXX表空间中的XXX数据页XXX偏移量的地方做了XXX更新。
所以MySQL意外宕机重启也没关系。只要在重启时解析redo log中的事务然后重放一遍。将Buffer Pool中的缓存页重做成脏页。后续再在合适的时机将该脏页刷入磁盘即可。
redo log 三种状态
redo log的刷盘时机
MySQL支持用户自定义在commit时如何将log buffer中的日志刷log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新log buffer到磁盘。
- 当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
- 当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
- 当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。