bin-log、redo-log、undo-log、MVCC 桃扇骨 2022-04-11 11:00 256阅读 0赞 # 重做日志(redo log) # ### 作用 ### 确保事务的持久性。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。 ### 内容 ### 物理格式的日志,记录的是物理数据页面的修改的信息,其redo log是顺序写入redo log file的物理文件中去的。 ### 产生时间 ### 事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。 ### 释放时间 ### 当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。 ### 对应的本地物理文件 ### * 默认情况下,对应的物理文件位于数据库的data目录下的ib\_logfile1&ib\_logfile2 innodb\_log\_group\_home\_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。 innodb\_log\_files\_in\_group 指定重做日志文件组中文件的数量,默认2 * 关于文件的大小和数量,由以下两个参数配置: innodb\_log\_file\_size 重做日志文件的大小。 innodb\_mirrored\_log\_groups 指定了日志镜像文件组的数量,默认1 ### redo-log落盘时间 ### * 在事物开始之后逐步写盘的。Innodb存储引擎先将重做日志写入innodb\_log\_buffer中。 * 然后会通过以下三种方式将innodb日志缓冲区的日志刷新到磁盘 1. Master Thread 每秒一次执行刷新Innodb\_log\_buffer到重做日志文件。 2. 每个事务提交时会将重做日志刷新到重做日志文件。 3. 当重做日志缓存可用空间 少于一半时,重做日志缓存被刷新到重做日志文件 由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb\_log\_buffer到重做日志文件是Master Thread线程的定时任务。 因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。 另外引用《MySQL技术内幕 Innodb 存储引擎》(page37)上的原话: 即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。 这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。 # 回滚日志(undo log) # ## 作用 ## Undo log可以用来做事务的回滚操作,保证事务的原子性。同时可以用来构建数据修改之前的版本,支持多版本读。 ## 内容 ## 逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的。 ### 产生时间 ### 事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性 ### 释放时间 ### 当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。 ### 实现MVCC的原理 ### * InnoDB表数据组织方式是主键聚簇索引。二级索引通过索引键值加主键值组合来唯一确定一条记录。聚簇索引和二级索引都包含了DELETED BIT标记位来标识记录是否被删除,真正的删除在事务commit之后且没有读会引用该版本数据的时候。在聚簇索引上还有一些额外信息会存储,6字节的DB\_TRX\_ID字段,表示最近一次插入或者更新该记录的事务ID。7字节的DB\_ROLL\_PTR字段,指向该记录的rollback segment的undo log记录。6字节的DB\_ROW\_ID,当有新数据插入的时候会自动递增。当表上没有用户主键的时候,InnoDB会自动产生聚集索引,包含DB\_ROW\_ID字段。 * 对于聚簇索引,更新是在原记录位置更新,通过记录指向undo log的隐藏列来重构早期版本的数据。但对于二级索引,是没有聚簇索引上的这些隐藏列的,因此无法在原记录位置更新。当二级索引更新的时候,需要将原记录标记为删除,再插入新的数据记录。当快照读通过二级索引读取数据发现deleted标识或者更新的时候,如果二级索引页上无法判断可见性,InnoDB会查看聚簇索引上的记录行,通过行上的DB\_TRX\_ID判断可见性,找到正确的可见版本数据。 * 当用mvcc读取的时候(row\_search\_mvcc),对于聚簇索引,当拿到一条记录后,会先通过函数lock\_clust\_rec\_cons\_read\_sees判断可见性,如果不可见会再构建老版本数据row\_vers\_build\_for\_consistent\_read。 ### undolog释放时间 ### * 由于Undo log会保留直到事务提交同时没有其他快照读引用后才会purge。所以需要尽量避免长语句或长事务的执行,避免因此导致的undo堆积或者undo链太长使读取变慢。 ## ReadView可见性判断 ## 如果记录trx\_id小于m\_up\_limit\_id或者等于m\_creator\_trx\_id,表明ReadView创建的时候该事务已经提交,记录可见。 如果记录的trx\_id大于等于m\_low\_limit\_id,表明事务是在ReadView创建后开启的,其修改,插入的记录不可见。 当trx\_id在m\_up\_limit\_id和m\_low\_limit\_id之间的时候,如果id在m\_ids数组中,表明ReadView创建时候,事务处于活跃状态,因此记录不可见。 # 二进制日志(binlog): # 作用: 用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。 用于数据库的基于时间点的还原。 内容: 逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句。 但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息。 在使用mysqlbinlog解析binlog之后一些都会真相大白。 因此可以基于binlog做到类似于oracle的闪回功能,其实都是依赖于binlog中的日志记录。 什么时候产生: 事务提交的时候,一次性将事务中的sql语句(一个事物可能对应多个sql语句)按照一定的格式记录到binlog中。 这里与redo log很明显的差异就是redo log并不一定是在事务提交的时候刷新到磁盘,redo log是在事务开始之后就开始逐步写入磁盘。 因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的,但是在开启了bin\_log的情况下,对于较大事务的提交,可能会变得比较慢一些。 这是因为binlog是在事务提交的时候一次性写入的造成的,这些可以通过测试验证。 什么时候释放: binlog的默认是保持时间由参数expire\_logs\_days配置,也就是说对于非活动的日志文件,在生成时间超过expire\_logs\_days配置的天数之后,会被自动删除。 对应的物理文件: 配置文件的路径为log\_bin\_basename,binlog日志文件按照指定大小,当日志文件达到指定的最大的大小之后,进行滚动更新,生成新的日志文件。 对于每个binlog日志文件,通过一个统一的index文件来组织。 其他: 二进制日志的作用之一是还原数据库的,这与redo log很类似,很多人混淆过,但是两者有本质的不同 作用不同:redo log是保证事务的持久性的,是事务层面的,binlog作为还原的功能,是数据库层面的(当然也可以精确到事务层面的),虽然都有还原的意思,但是其保护数据的层次是不一样的。 内容不同:redo log是物理日志,是数据页面的修改之后的物理记录,binlog是逻辑日志,可以简单认为记录的就是sql语句 另外,两者日志产生的时间,可以释放的时间,在可释放的情况下清理机制,都是完全不同的。 恢复数据时候的效率,基于物理日志的redo log恢复数据的效率要高于语句逻辑日志的binlog 关于事务提交时,redo log和binlog的写入顺序,为了保证主从复制时候的主从一致(当然也包括使用binlog进行基于时间点还原的情况),是要严格一致的,MySQL通过两阶段提交过程来完成事务的一致性的,也即redo log和binlog的一致性的,理论上是先写redo log,再写binlog,两个日志都提交成功(刷入磁盘),事务才算真正的完成。
还没有评论,来说两句吧...