Mysql----日志系统redo log 和 bin log

目录

一、mysql大概架构

二、redo log(重做日志)

2.1 WAL机制 (Write-Ahead Logging )

2.2  redo log结构

2.3  redo log 和 crash safe

三、bin log (归档日志)

3.1  bin log有什么用

3.2  两阶段提交

3.3  为什么需要两阶段提交

小结


一、mysql大概架构

Mysql----日志系统redo log 和 bin log_第1张图片

大体来说,mysql可以分为Server层存储引擎两部分。

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能。

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

而 bin log 是存在于 Server层的,即表示无论什么存储引擎,都有binlog 日志。

但redo log 是存在于 innoDB存储引擎内的,即只有innoDB才有redo log日志。

 

二、redo log(重做日志)

当我们对Mysql的数据进行更新时,如果每一次的更新都需要

  1. 写进磁盘
  2. 然后磁盘也要找到对应的那条数据
  3. 然后再更新

那整个过程的IO成本、查找成本都很高,为了解决问题,Mysql就用了WAL机制。

2.1 WAL机制 (Write-Ahead Logging )

现在mysql的增删改都是在内存BufferPool缓冲池中完成的,如下图。

Mysql----日志系统redo log 和 bin log_第2张图片

WAL的关键点就是 先写日志,再写磁盘。

具体来说,当有一条记录需要更新时,InnoDB引擎就会把记录写到 redo log里面,并更新内存,这个时候更新就算完成了。

同时,InnoDB会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。

有人可能问,redo log 日志也是存在于磁盘中啊,既然日志是存在磁盘中,mysql数据也是存在磁盘中,都是要写磁盘啊,那还搞那么多干嘛。

区别就是,redo log是顺序写盘的,速度很快,redo log的写方式是顺序IO,更新磁盘操作是随机IO,随机IO和顺序IO相比,有一个寻址的过程,所以顺序写盘更快。

所以WAL机制是通过用一个更快的方式记录更新数据(写redo log),然后等系统空闲的时候,再把redo log的更新数据写回到mysql磁盘数据中

 

(接上,redo log的持久化

持久化策略通过参数 innodb_flush_log_at_trx_commit 控制。

设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在内存的 redo log buffer 中 ; MySQL 崩溃就会丢失。
设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘(将 redo log buffer 中的操作全部进行持久化,可能会包含其他事务还未提交的记录);断电也不会丢失。
设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。MySQL 崩溃不会丢失,断电会丢失。

这个参数建议设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

2.2  redo log结构

redo log包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

为了确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用)。因为MySQL是工作在用户空间的,MySQL的log buffer处于用户空间的内存中。要写入到磁盘上的log file中(redo:ib_logfileN文件),中间还要经过操作系统内核空间的os buffer,调用fsync()的作用就是将OS buffer中的日志刷到磁盘上的log file中。

也就是说,从redo log buffer写日志到磁盘的redo log file中,过程如下: 

Mysql----日志系统redo log 和 bin log_第3张图片

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。

Mysql----日志系统redo log 和 bin log_第4张图片

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件

write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。当然redo log有刷盘机制,例如 每次commit 刷一次盘,或者每几秒刷一次,并不一定会等到redo log没空间了才进行刷盘的。

 

2.3  redo log 和 crash safe

Innodb引擎具有crash-safe能力,这种crash-safe 能力,是通过引擎层的redo log 来实现的。

事务在提交写入磁盘前,会先写到redo log里面去。

为什么不直接写到Mysql中去?这是因为数据写到Mysql中去,需要找到磁盘的Mysql的对应的页,涉及磁盘的随机I/O访问,涉及磁盘随机I/O访问是非常消耗时间的一个过程,相比这个时间,先写入redo log,后面再找合适的时机刷盘,能大大提升效率。

另外,如果Mysql 进程异常重启了,系统会自动去检查redo log,将未写入到Mysql的数据从redo log恢复到Mysql中去。

什么意思?就是 redo log中会记录还没同步到数据磁盘中的 对mysql的操作 。再细一点说就是,当有更新操作,先对内存BufferPool中的数据进行修改(若BufferPool没有要修改的数据,那就从磁盘读到BufferPool中),那么修过后的BufferPool中的数据就会和磁盘中的数据不一致(因为还没同步到磁盘嘛),所以就需要在redo log中写下做过哪些修改操作,等系统时机到了,再把这些修改操作同步到磁盘中。若在还没来得及同步到磁盘,mysql就宕机了,那咋办,mysql宕机了,意味着内存里的BufferPool里的数据就全没了,这个时候,mysql就只需要调出磁盘里的redo log,看看有哪些操作是还没同步到磁盘的,就找到了宕机之前,用户做了的操作。这就是 crash-safe。

 

三、bin log (归档日志)

前面我们讲过,MySQL 整体来看,其实就有两块:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。上面我们聊到的 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。

binlog记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作,因为这类操作对数据本身并没有修改。

3.1  bin log有什么用

binlog是Mysql server层的日志,主要用于数据误删后进行数据恢复,另外,主从复制也需要依靠binlog。

3.2  两阶段提交

为什么必须有“两阶段提交”呢?这是为了让两份日志(redo log 和 bin log)之间的逻辑一致。前面我们说过了,binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的 DBA 承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有 binlog。

当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。

两阶段提交的大致流程是:

1. 有新的更新操作时,先把操作记录到 redo log中。

2.但由于事务还没提交,所有此时redo log所记录的新的更新操作是被标记为prepare状态。

3. 然后执行器后续会生成这个操作的bin log,并把bin log写入磁盘。

4.最后提交事务,再把刚刚写入redo log的操作改成 commit状态,表示更新完成。

 

具体例子,我们看看执行这条sql语句后,redo log 和bin log各做了什么?

mysql> update T set c=c+1 where ID=2;
  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个c值加上 1,比如原来是 c=N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

3.3  为什么需要两阶段提交

由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。

仍然用前面的 update 语句来做例子。假设当前 ID=2 的行,字段 c 的值是 0,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?

  1. 先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
  2. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

简单说,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

 

小结

介绍了 MySQL 里面最重要的两个日志,即物理日志 redo log 和逻辑日志 binlog。

1. redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

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

你可能感兴趣的:(Mysql)