InnoDB学习笔记(3)Redo log

MySQL · 引擎特性 · InnoDB redo log漫游
MySQL · 引擎特性 · The design of mysql8.0 redolog

LSN(log sequence number)

用于记录日志序号,它是一个不断递增的 unsigned long long 类型整数。在 InnoDB 的日志系统中,LSN 无处不在,它既用于表示修改脏页时的日志序号,也用于记录checkpoint,通过LSN,可以具体的定位到其在redo log文件中的位置。

为了管理脏页,在 Buffer Pool 的每个instance上都维持了一个flush list,flush list 上的 page 按照修改这些 page 的LSN号进行排序。因此定期做redo checkpoint点时,选择的 LSN 总是所有 bp instance 的 flush list 上最老的那个page(拥有最小的LSN)。

WAL

MySQL · 引擎特性 · WAL那些事儿

采用WAL 的方式进行写入数据:所有的数据都先写入到redo log, 然后后续再从buffer pool 刷脏到数据页又或者是备份恢复的时候从redo log 恢复到buffer pool, 然后在刷脏到数据页。每次事务提交时需要持久化 redo log 才能保证事务不丢。

WAL很重要的一点是将随机写转换成了顺序写,而顺序写的性能远远大于随机写。且延迟刷脏起到了合并多次修改的效果,避免频繁写数据文件造成的性能问题。

  1. 当用户线程产生日志的时候,首先缓存在一个线程私有的变量(mtr)里面,只有完成某些原子操作(例如完成索引分裂或者合并等)的时候,才把日志提交到全局的日志缓存区中。全局缓存区的大小(innodb_log_file_size)可以动态配置。当线程的事务执行完后,会按照当前的配置(innodb_flush_log_at_trx_commit)决定是否需要把日志从缓冲区刷到磁盘。
  2. 当把日志成功拷贝到全局日志缓冲区后,会继续把当前已经被修改过的脏页加入到一个全局的脏页链表中。这个链表有一个特性:按照最早被修改的时间排序。例如,有数据页A,B,C,数据页A早上9点被第一次修改,数据页B早上9点01分被第一次修改,数据页C早上9点02分被第一次修改,那么在这个链表上数据页A在最前,B在中间,C在最后。即使数据页A在早上9点之后又一次被修改了,他依然排在B和C之前。在数据页上,有一个字段来记录这个最早被修改的时间:oldest_modification,只不过单位不是时间,而是lsn,即从数据库初始化开始,一共写了多少个字节的日志,由于其是一个递增的值,因此可以理解为广义的时间,先写的数据,其产生的日志对应的lsn一定比后写的小。在脏页列表上的数据页,就是按照oldest_modification从小到大排序,刷脏页的时候,就从oldest_modification小的地方开始。checkpoint就是脏页列表中最小的那个oldest_modification,因为这种机制保证小于最小oldest_modification的修改都已经刷入磁盘了。这里最重要的是,脏页链表的有序性,假设这个有序性被打破了,如果数据库异常crash,就会导致数据丢失。例如,数据页ABC的oldest_modification分别为120,100,150,同时在脏页链表上的顺序依然为A,B,C,A在最前面,C在最后面。数据页A被刷入磁盘,然后checkpoint被更新为120,但是数据页B和C都还没被刷入磁盘,这个时候,数据库crash,重启后,从checkpoint为120开始扫描日志,然后恢复数据,我们会发现,数据页C的修改被恢复了,但是数据页B的修改丢失了。

5.6版本

需要两个全局的mutex:

  1. 每一个用户连接有一个线程, 要写入数据之前必须先获得log_sys_t::mutex, 用来保证只有一个用户线程在写入log buffer 那么随着连接数的增加, 这个性能必然会受到影响
  2. 同样的在把已经写入完成的redo log 加入到flush list 的时候, 为了保证只有一个用户线程从log buffer 上添加buffer 到flush list, 因此需要去获得log_sys_t::flush_order_mutex来保证

8.0版本 - 无锁实现

在8.0 的实现中, 把一个write redo log的操作分成了几个阶段

  1. 获得写入位置, 实现: 用户线程(在每一个线程获得自己要写入的lsn的位置)
  2. 写入数据到log buffer实现: 用户线程(因为每个线程的写入位置不同,所以可以并发写)
  3. log buffer中的数据写入到redo log文件 实现: log writer(log writer会检查log buffer是否有空洞,只有在log buffer连续的时候才会向前推进)
  4. redo log中的page cache flush 到磁盘 实现: log flusher
  5. redo log中的log buffer对应的脏页无序地添加到flush list
  6. 更新可以打checkpoint位点信息recent_closed实现: log closer
  7. 根据recent_closedcheckpoint信息 实现: log checkpointer

笔记

  • 打checkpoint 以后, 在checkpoint 之前的redo log 应该是都可以删除的, 因此我们必须保证打的checkpoint lsn 的这个点之前的redo log 已经将对应的page flush到磁盘上了
  • 用户只需要写入到redo log, 并且确认redo log 已经flush 了以后, 就直接返回了
  • 数据的更新操作最先写到log buffer中,然后定期地flush到磁盘的redo log文件中。触发写redo的条件
    • Redo log buffer空间不足
    • 事务提交
    • 后台线程
    • 做checkpoint
    • 实例shutdown时
    • binlog切换时

你可能感兴趣的:(InnoDB学习笔记(3)Redo log)