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,说明缓冲区有多少脏页数据被刷到磁盘上了。
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层)
这两个文件的写入,相当于分布式事务的