mysql 两种重要的日志

前言

这个不用多说,两种重要的日志分别是redo log 和 binlog,这两种,下面分别来结束。

正文

先来redo log, redo log 是inno db 引擎特有的,运用了一种wal 技术,全称是: write-ahead logging.

它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。

简单来说就是先写内存,然后写redo log,这样就结束了,而不需要立马写入到磁盘中去,避免了频繁的io操作开销,这里面的优点就不言而喻了。

那么redo log 是怎么样的呢?

redo log 一共4个文件,如上文所述,每个文件大小是1个g。

上图中有两个字段,分别是write pos(write position) 和 check point,

那么redo log 也不可能一直不将数据写入到磁盘中对吧。

那么write pos一直向前,check point就再后面一直更新数据库,这样就形成了一个环。

当write pos 追赶到check point的时候那么这个时候怎么办呢? 这个时候redo log就只能不让提交了,不让提交的话,那么这个时候就所有的操作都进入了等待状态了。

需要等待check point 和 write pos 距离拉开后,然后数据库才能继续操作。

这样即使重启,那么也不会有啥问题,因为反正redo log已经写了,只要把redo log 更新即可。

redo log,全称是ready do log,就是正要准备去做的日志。

前面提及到redo log 是inno db的日志,那么service 层有没有日志,也有叫做bin log,全称binary log,用于归档使用的。

这两种日志有以下三点不同。

  1. redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。

  2. redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的
    是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。

针对第二点进行解释一下:

1. Redo log:
   - Redo log 是用于数据库的事务日志,记录了数据库中发生的每个数据修改操作,包括数据更改前和更改后的内容。这样可以通过重做日志回滚未提交的事务或者恢复系统崩溃后的数据一致性。
   - Redo log 是在事务执行过程中生成并写入磁盘的,在数据库引擎中起到数据保护和恢复的作用。
  
2. Binlog(二进制日志):
   - Binlog 记录了数据库执行的所有语句,可以是创建表、更新数据或者删除数据的操作,并未记录每个数据修改的具体内容。
   - Binlog 的内容是与数据库服务器的操作相关联的 SQL 语句,可以用于灾难恢复、备份、数据库复制等操作,不同服务器之间可以通过 Binlog 来保持数据的同步。


总的来说,Redo log 记录的是数据修改的物理操作记录,而 Binlog 记录的是逻辑操作的 SQL 语句记录。两者虽然存在共同点,但在数据恢复和保护上有着不同的功能和作用。
假设有一个数据库表,其中有一行数据的内容为 "John Doe",现在执行了一个更新操作,将该行数据修改为 "Jane Smith"。这个更新操作会生成一个 Redo log 记录,其中包含了修改前的数据 "John Doe" 和修改后的数据 "Jane Smith",以及相关的事务信息。这样,如果系统发生故障或者需要回滚操作,数据库可以通过 Redo log 中的记录来恢复数据到更新之前的状态。

在MySQL中,Redo日志会记录主键索引和聚簇索引页的修改,但不会记录二级索引页的修改。 Redo日志会记录对数据页的修改,而二级索引页属于辅助索引,不包含在Redo日志中。

然后change buffer 只是针对二级索引的更新有效,对主键索引这种一级索引是无效的,这个需要理解,redo 日志会记录数据更新前的数据和数据更新后的数据。

  1. redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件
    写到一定大小后会切换到下一个,并不会覆盖以前的日志。

有了对这两个日志的概念性理解,我们再来看执行器和InnoDB引擎在执行这个简单的update语
句时的内部流程。

  1. 执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一
    行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然
    后再返回。

  2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行
    数据,再调用引擎接口写入这行新数据。

  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处
    于prepare状态。然后告知执行器执行完成了,随时可以提交事务。

  4. 执行器生成这个操作的binlog,并把binlog写入磁盘。

  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更
    新完成

将redo log的写入拆成了两个步骤:prepare和commit,这就是"两阶段提交"。

为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。要说明这个问题,我们得
从文章开头的那个问题说起:怎样让数据库恢复到半个月内任意一秒的状态? 怎
前面我们说过了,binlog会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的DBA承
诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有binlog,同时系统会定期
做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。

当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数
据,那你可以这么做:

首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备
份恢复到临时库;

然后,从备份的时间点开始,将备份的binlog依次取出来,重放到中午误删表之前的那个时
刻。

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

你可能会说,这个概率是不是很低,平时也没有什么动不动就需要恢复临时库的场景呀?
其实不是的,不只是误操作后需要用这个过程来恢复数据。当你需要扩容的时候,也就是需要再
多搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用binlog来
实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况。
简单说,redo log和binlog都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保
持逻辑上的一致。

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

redo log 和 binlog是怎么关联起来的?

回答:它们有一个共同的数据字段,叫XID。崩溃恢复的时候,会按顺序扫描redo log:

如果碰到既有prepare、又有commit的redo log,就直接提交;

如果碰到只有parepare、而没有commit的redo log,就拿着XID去binlog找对应的事务。

处于 : prepare阶段的 阶 redo log加上完整 加 binlog,重启就能恢
复,而不是将redo log删除呢,MySQL为什么要这么设计?

在时刻B,也就是binlog写完以后MySQL发生崩溃,这时候binlog已经写入了,之后就会被从库(或者用这个binlog恢复出来的库)使用。

如果这样的话,为什么还要两阶段提交呢?干脆先 : redo log写完,再写 写
binlog。崩溃恢复的时候,必须得两个日志都完整才可以。是不是一样的逻辑?

对于InnoDB引擎来说,如果redo log提交完成了,事务就不能回滚(如果这还允许回滚,就可能
覆盖掉别的事务的更新)。而如果redo log直接提交,然后binlog写入的时候失败,InnoDB又回
滚不了,数据和binlog日志又不一致了。

正常运行中的实例,数据写入后的最终落盘,是从 : redo log更新过来的 更
还是从 还 buffer pool更新过来的呢?

实际上,redo log并没有记录数据页的完整数据,所以它并没有能力自己去更新磁盘数据页,也
就不存在“数据最终落盘,是由redo log更新过去”的情况。

  1. 如果是正常运行的实例的话,数据页被修改以后,跟磁盘的数据页不一致,称为脏页。最终
    数据落盘,就是把内存中的数据页写盘。这个过程,甚至与redo log毫无关系。

  2. 在崩溃恢复场景中,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就
    会将它读到内存,然后让redo log更新内存内容。更新完成后,内存页变成脏页,就回到了
    第一种情况的状态。

下一结介绍一下为什么很多公司选择读可提交作为模式。

posted @ 2024-08-04 20:57  敖毛毛  阅读(26)  评论(0编辑  收藏  举报