30 March 2020

Redo Log(环形存储结构)

redo 日志是为了系统崩溃后恢复脏页用的,如果这个脏页可以被刷新到磁盘上,那么他就可以功成身退,被覆盖也就没事啦。它记录的是在某个数据页上做了什么修改, 这个日志会携带一个LSN, 同时每个数据页上也会记录一个LSN(日志序列号).

这个日志是循环写入的. [先写 ib_logfile0,再写 ib_logfile1,等 ib_logfile1 写满了,再写 ib_logfile0] [环形]

那这样就会存在一个问题,如果 ib_logfile1 写满了,再写 ib_logfile0,之前 ib_logfile0 的内容不就被覆盖而丢失了吗?这就是 checkpoint 的工作啦。

这里有两个关键位置点:

  • write pos 当前记录的位置, 一边写以便后移.
  • checkpoint 是当前要擦除的位置, 擦除记录前要把记录更新到数据文件.

redo log日志是不断递增的,mysql为其取名lsn,redo log先写log buffer,之后才会被刷到磁盘的redo日志文件,mysql为其取了一个名字flush_to_disk_lsn,说明缓冲区有多少脏页数据被刷到磁盘上了。

企业微信截图_6c333d62-f551-409e-8592-66f2eef50aaf.png

Undo log

  • 提供回滚
  • 多个行版本控制MVCC

可以认为当 delete 一条记录时,undo log 中会记录一条对应的 insert 记录,反之亦然,当 update 一条记录时,它记录一条对应相反的 update 记录。

MVCC

版本链:对于该记录的每次更新,都会将值放在一条 undo 日志中,算是该记录的一个旧版本,随着更新次数的增多,所有版本都会被 roll_pointer 属性连接成一个链表,即为版本链。(一个记录有多个人读,这些读命令都是同一个tractionId,之后undo log有多个trancationId的版本组成的链)

WAL

先写日志,再写磁盘。

在对数据页进行修改时,通过将”修改了什么“这个操作记录在日志中,而不必马上将更改内容刷新到磁盘上,从而将随机写转换为顺序写。

由此引出的问题:内存中的数据页和磁盘上的数据页内容不一致,此时将内存中的这种数据页称为脏页。

脏页

当内存数据页和磁盘数据页内容不一致的时候, 将内存页称为”脏页”. 内存数据页写入磁盘后, 两边内容一致, 此时称为”干净页”. 将内存数据页写入磁盘的这个操作叫做”刷脏页”(flush).

InnoDB是以缓冲池(Buffer Pool)来管理内存的,缓冲池中的内存页有三种状态

  • 未被使用
  • 已被使用, 并且是干净页
  • 已被使用, 并且是脏页

由于InnoDB的策略通常是尽量使用内存, 因此长时间运行的数据库中的内存页基本都是被使用的, 未被使用的内存页很少.

刷脏页(flush)的时机–引起mysql抖动

  • Redo Log buffer写不下了, 需要将 checkpoint 向前推进, 以便继续写入日志

    checkpoint 向前推进时, 需要将推进区间涉及的所有脏页刷新到磁盘.

  • Redo log buffer内存不足, 来一个大事务buffer放不下,需要在redo log buffer淘汰一些内存页(最久未使用的)给别的数据页使用.

    此时如果是干净页, 则直接拿来复用.

    如果是脏页, 则需要先刷新到磁盘(此时大事务直接写入磁盘, 不用管Redo Log, 后续Redo Log刷脏页时会判断对应数据页是否已刷新到磁盘), 使之成为干净页再拿来使用.

  • 定时任务10s

  • 数据库正常关闭

    此时需要将所有脏页刷新到磁盘.

InnoDB需要控制脏页比例来避免Redo Log写满以及单次淘汰过多脏页过多的情况.

避免方式:

redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。

sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。

redo log(innodb独有,引擎层) binlog(server层)

这两个文件的写入,相当于分布式事务的



blog comments powered by Disqus