MySQL三个日志的作用

  • MySQL有三个日志:undo log、redo log和bin log

undo log

undo log的作用:

  1. 用于实现事务的原子性,它记录了执行更新操作前的数据,可以实现事务的回滚操作。
  2. 配合ReadView实现MVCC(多版本并发控制),从而实现快照读下的读提交和可重复读两个事务隔离级别

  • undo log的工作原理

undo日志主要记录的是回滚操作所需的一些信息,比如:
插入一条记录时将主键记录下来,回滚时只有删除该主键对应的记录;
删除一条记录时需要将这条记录的内容全部记录下来,回滚时再执行插入操作
更新一条记录时将旧值记录下来,回滚时就可以直接根据旧值进行更新

undo log版本链

当我们对记录进行修改时,会形成undo log版本链

undo log的刷盘时机

undo log和数据页一样,在buffer pool中都有专门的页记录对undo 版本链和数据的修改信息,并且这些修改也会记录到redo log保证持久化。

redo log

redo log的作用

当buffer pool中出现脏页时,**MySQL不会马上刷新脏页到磁盘,而是会先在redo log中记录是哪个表空间中的哪个数据页的多少偏移量处进行了修改(WAL:Write-Ahead logging),然后在合适的时机再将脏页进行刷盘。**一个事务中可能涉及这样的多个修改日志,最终事务提交,redo log持久化到磁盘,尽管这时可能buffer pool中的脏页还没刷盘,但是MySQL重启后(buffer pool中脏页丢失了),可以通过redo log来将数据恢复到最新状态

undo log和redo log的区别

undo log记录的是事务完成后的数据状态,记录的是更新之后的值,
而redo log记录的是事务执行前的数据状态,记录的是更新之前的值
事务执行过程中MySQL发生崩溃,重启后会通过undo log回滚事务
事务提交后MySQL发生奔溃,重启后会通过redo log恢复事务,因此使得MySQL有crash-safe的崩溃安全能力

总结redo log的两个作用

  1. 使得MySQL有crash-safe的能力。redo log记录了事务执行过程中的修改操作,如果事务提交后MySQL奔溃,重启后可以通过redo log来使数据恢复到最新状态。
  2. 另外,还使得数据的写操作从随机写变成顺序写。因为redo log是追加写,不同于直接对数据在磁盘上进行的写操作,那样需要在磁盘找到对应的记录,是随机写的。

redo log buffer

  • redo log不是直接写入磁盘的,因为这样做会产生大量磁盘IO,并且磁盘的运行速度比内存要慢,所以redo log也有自己的缓冲区

redo log buffer pool中的数据什么时候刷盘?

四种情况
  • MySQL 正常关闭时;
  • 当 redo log buffer 中的数据达到其空间的一半时;
  • InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。
  • 默认情况下,每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由 innodb_flush_log_at_trx_commit 参数控制)。
关于innodb_flush_log_at_trx_commit 参数
  • 默认情况为1,也就是在事务提交之后就刷盘
  • 如果为0,事务提交的时候不会刷盘,innoDB后台线程会每隔一秒调用write()写到操作系统的文件缓存中,然后调用fsync()再将其写到磁盘。因此MySQL进程的奔溃会导致上一秒所有事务数据丢失。
  • 如果为2,事务提交的时候只会将redo log buffer中的数据写入到操作系统的文件缓存中,innoDB后台线程会每隔一秒调用fsync将文件缓存中的数据写到磁盘,因此MySQL进程的奔溃不会丢失数据,只有操作系统奔溃或者断点的情况下,上一秒的事务数据才会丢失

  • 性能:0 > 2 > 1
  • 安全:1 > 2 > 0

redo log的循环写

innnoDB存储引擎有一个重做日志文件组,对应两个redo日志文件 ib_logfile0 和 ib_logfile1 ,它们的大小相同,它们使得redo log是一个循环写的状态
红色部分记录新的更新操作,蓝色部分是待刷盘的脏页记录
当write pos追上check point时,红色部分没有位置,说明redo log文件满了,由于redo log是保存了未被撒呼入磁盘的脏页数据,所以这时会将buffer pool中的脏页刷盘,然后就可以向前移动check point

bin log

bin log记录了数据库表结构和表数据修改的信息,是在MySQL的server层中产生的,事务提交后,会将事务执行期间产生的bin log写入bin log文件

bin log和redo log的区别

  1. 使用对象不同

bin log是MySQLserver层产生的,所有存储引擎都可以用,而redo log是innoDB存储引擎实现的日志

  1. 文件格式不同

binlog有三种文件格式:STATEMENT(默认),ROW和MIXED

  • STATEMENT格式记录了每一条修改数据的SQL,相当于记录了逻辑操作,所以bin log被称为逻辑日志

但是会有一个文件,就是使用这种格式时,主库执行从库的bin log会出现动态函数比如now等执行的结果不一致

  • ROW格式记录的是记录被修改成什么样子,这种格式不是逻辑日志了。虽然不会出现像STATEMENT格式下动态函数的问题,但是会导致bin log文件过大,比如一条update语句,可能修改多条记录,于是会记录多条记录的变化结果,而使用STATEMENT格式只需要一条update语句
  • MIXED格式会根据情况选择STATEMENT格式格式和ROW格式中的一种。

redo log是一种物理日志,它记录了对那个表空间哪个数据页多少偏移量的地方做了什么修改

  1. 写入方式不同

redo log是循环写,写满了从头开始写,而bin log是追加写,也就是写满就重新创建一个文件继续写

  1. 用途不同

bin log用于主从复制,备份恢复
redo log用于事务提交后,MySQL奔溃重启后的数据恢复

异步复制模型

主从复制是异步的,也就是主库上执行事务操作的线程不会等待复制bin log的线程同步完成,分为三步:

  1. 主库事务提交后先写入bin log
  2. 主库的log dump线程会发生binlog日志给从库,从库创建一个IO线程将其写入到中继日志中
  3. 从库回放binlog

  • 主从复制后,可以实现主库进行写操作,从库进行读操作,这样的好处是主库执行的写操作如果加了表锁或记录锁也不会影响从库的读操作

  • 从库不是越多越好的,因为处理大量的从库的IO请求会消耗主库的资源,也会受限于网络带宽,一般都是1主,2-3从(1主2从1备主)

同步复制模型和半同步复制模型

  • MySQL默认的主从复制模型是异步模型,主库提交事务的线程并不会等待bin log成功同步到各从库,就返回客户端结果,缺点就是主库一旦宕机,就可能出现数据丢失
  • 和异步模型相对的是同步模型,同步模型下,,主库提交事务的线程需要等待bin log成功同步到各从库,才会返回客户端结果,这样的缺点就是任何一个从库复制bin log的过程出现问题,都会影响主库的操作。而主库发生宕机,也会出现数据丢失。
  • 还有一种模型是MySQL5.7后出来的半同步复制,主库提交事务的线程在有一部分从库成功复制了bin log之后返回客户端结果。它兼顾了异步模型和同步模型,这样,即使主库宕机也不会发生数据丢失,因为至少还有一个从库有最新的数据。

bin log什么时候刷盘

事务执行时,bin log写入到bin log cache中,当事务提交后,则会调用write()将bin log cache中的数据写入到操作系统的page cache中,并清空bin log cache,后面,再通过fsync()将page cache中的数据写入磁盘

通过sync_binlog参数来控制bin log什么时候刷盘

  • sync_binlog = 0 的时候,表示每次提交事务都只 write,不 fsync,后续交由操作系统决定何时将数据持久化到磁盘;
  • sync_binlog = 1 的时候,表示每次提交事务都会 write,然后马上执行 fsync;(最多丢失一个事务的bin log)
  • sync_binlog =N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync

两阶段提交

redo log会影响主库的数据,bin log会影响从库的数据,为了防止redo log 和bin log之间出现逻辑不一致的情况,也就是一个刷盘成功,一个没有刷盘成功的情况,这样会导致主从不一致,因此MySQL使用了内部事务XA,并且这个事务的分两个阶段提交的。

在prepare阶段,事务提交后,事务XA的id会写入redo log,并且redo log的事务状态设置为prepare,然后redo log持久化到磁盘。后面,在bin log也将XA写入,接着bin log持久化到磁盘,redo log中的事务状态才设置为commit,当然,此时这个状态即使没有持久化到磁盘也没有关系,因为只要bin log持久化到磁盘成功,该事务就被认为已经执行成功。

在两阶段提交的情况下,无论MySQL何时奔溃重启,都会去扫描拿到redo log中内部事务的id然后去bin log中看看能不能找到相同的内部事务id,如果有说明bin log写入磁盘成功,因此说明事务执行成功,两份日志保存一致,可以提交事务,否则,则回滚事务。

  • 需要注意,redo log可以在事务执行的过程中由后台线程将redo log buffer中的日志刷新到磁盘,但是bin log一定要在事务提交之后才可以刷新到磁盘

两阶段提交的问题

  1. 磁盘IO次数高。一般为了避免日志丢失,都会设置redo log和bin log在事务提交的时候刷盘,但是这样会造成大量磁盘IO,从而降低了性能
  2. 锁竞争激烈。两阶段提交可以解决单个事务的两个日志内容一致,但是对于多事务的情况下,需要加一个锁来保证这多个事务的提交顺序的有序性,但是会导致对锁的争用,性能不佳。

组提交

为了解决两阶段提交在多事务情况下出现的问题
在两阶段提交中,prepare阶段保持不变,commit阶段再分为三个阶段:

  1. flush阶段:多个事务按照顺序将bin log写入操作系统的page cache
  2. sync阶段:对page cache中的 bin log文件做fsync操作,使得多个事务的bin log合并为一次磁盘IO
  3. commit阶段:各个事务按照顺序做InnoDB commit阶段

image.png
每个阶段都对应了一个队列,并由锁保证事务写入的顺序,因此锁的粒度减小了,从锁住提交事务的整个过程变为了对每个队列进行锁保护。

  • 除了bin log组提交,MySQL5.7之后还有redo log组提交,它将redo log的刷盘操作推迟到了flush之中,sync阶段之前,对redo log进行了一次组写入。

如何减少MySQL磁盘IO次数?

MySQL磁盘IO次数高主要是因为redo log和bin log的刷盘需要,所以我们可以通过调节一些参数来延迟redo log和bin log的刷盘时机

  1. binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,前者控制在组提交过程中,等待N微妙后,才将一组bin log从操作系统的page cache中刷盘;后者则控制在组提交过程中,等待N个事务的bin log之后再刷盘
  2. sync_binlog 则和 binlog_group_commit_sync_no_delay_count 参数是一个控制内容,不过是要先满足了sync_binlog再进入binlog_group_commit_sync_no_delay_count的逻辑
  3. innodb_flush_log_at_trx_commit 设置为2,这样,每次事务提交后不会立马刷盘,而是write到page cache,然后交由操作系统来决定何时刷盘
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值