Mysql篇-三大日志

海哥  金牌会员 | 2024-11-14 09:38:22 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 826|帖子 826|积分 2478

概述


  • undo log(回滚日志):是 Innodb 存储引擎层天生的日志,实现了事件中的原子性,紧张用于事件回滚和 MVCC。
  • redo log(重做日志):是 Innodb 存储引擎层天生的日志,实现了事件中的持久性,紧张用于掉电等故障恢复;
  • binlog (归档日志):是 Server 层天生的日志,紧张用于数据备份和主从复制;
回滚日志(undo log)

作用


  • 保存了事件发生之前的数据的一个版本,可以用于回滚,保障原子性
  • 实现多版本并发控制下的读(MVCC)的关键因素之一,也即非锁定读,MVCC通过Read View + undolog的版本链实现,可以详细看MVCC的快照读
内容

逻辑格式的日志,在执行 undo 的时间,仅仅是将数据从逻辑上恢复至事件之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log 的。
每当 InnoDB 引擎对一条记载举行操作(修改、删除、新增)时,要把回滚时需要的信息都记载到 undo log 里,比如:

  • 插入insert一条记载时,要把这条记载的主键值记下来,这样之后回滚时只需要把这个主键值对应的记载删掉delete就好了;
  • 删除delete一条记载时,要把这条记载中的内容都记下来,这样之后回滚时再把由这些内容组成的记载插入insert到表中就好了;
  • 更新一条记载时,要把被更新的列的旧值记下来,这样之后回滚时再把这些列更新为旧值就好了。
什么时间产生

事件开始之前,MySQL 会先记载更新前的数据到 undo log 日志文件里面,当事件回滚时,可以利用 undo log 来举行回滚。同时undo 也会产生 redo 来保证undo log的可靠性。

什么时间刷盘

undo log 和数据页的刷盘计谋是一样的,都需要通过 redo log 保证持久化。产生undo日志的时间,同样会伴随类似于掩护事件持久化机制的redolog的产生。
buffer pool 中有 undo 页,对 undo 页的修改也都会记载到 redo log。redo log 会每秒刷盘,提交事件时也会刷盘,数据页和 undo 页都是靠这个机制保证持久化的,详细看下面内容。
重做日志(redo log)

作用


  • 确保事件的持久性。

    • 为了防止断电导致数据丢失的问题,当有一条记载需要更新的时间,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的情势记载下来,这个时间更新就算完成了。也就是说, redo log 是为了防止 Buffer Pool 中的脏页丢失而计划的。
    • 在重启mysql服务的时间,根据redo log举行重做,从而达到事件的持久性这一特性。

  • 将写操作从「随机写」酿成了「次序写」,提升 MySQL 写入磁盘的性能。
内容

物理格式的日志,记载的是物理数据页面的修改的信息,其 redo log 是次序写入redo log file 的物理文件中去的。同时,在内存修改 Undo log 后,也需要记载undo log对应的 redo log。
redo log 和 undo log 区别:

  • redo log 记载了此次事件完成后的数据状态,记载的是更新之后的值;
  • undo log 记载了此次事件开始前的数据状态,记载的是更新之前的值;
什么时间产生

事件开始之后就产生redo log,redo log的落盘并不是随着事件的提交才写入的,而是在事件的执行过程中,便开始写入redo log文件中。
事件提交之前发生了崩溃,重启后会通过 undo log 回滚事件,事件提交之后发生了崩溃,重启后会通过 redo log 恢复事件,如下图:

redo log 要写到磁盘,数据也要写磁盘,为什么要多此一举?
写入 redo log 的方式使用了追加操作, 以是磁盘操作是次序写,而写入数据需要先找到写入位置,然后才写到磁盘,以是磁盘操作是随机写。磁盘的「次序写 」比「随机写」 高效的多,因此 redo log 写入磁盘的开销更小。
什么时间刷盘

实际上, 执行一个事件的过程中,产生的 redo log 也不是直接写入磁盘的,因为这样会产生大量的 I/O 操作,而且磁盘的运行速度远慢于内存。
redo log有一个缓存区 Innodb_log_buffer,Innodb_log_buffer 的默认巨细为 16M,每当产生一条 redo log 时,会先写入到 redo log buffer,后续再持久化到磁盘。

然后会通过以下三种方式将innodb log buffer的日志革新到磁盘:

  • MySQL 正常关闭时;
  • 当 redo log buffer 中记载的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;
  • InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。
  • 每次事件提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘。
因此redo log buffer的写盘,并不一定是随着事件的提交才写入redo log文件的,而是随着事件的开始,逐步开始的。
即使某个事件还没有提交,Innodb存储引擎仍然每秒会将redo log buffer革新到redo log文件。
这一点是必须要知道的,因为这可以很好地解释再大的事件的提交(commit)的时间也是很短暂的。
redolog的文件

两个 redo 日志的文件名叫 :ib_logfile0 和 ib_logfile1。
redo log文件组是以循环写的方式工作的, InnoDB 存储引擎会先写 ib_logfile0 文件,当 ib_logfile0 文件被写满的时间,会切换至 ib_logfile1 文件,当 ib_logfile1 文件也被写满时,会切换回 ib_logfile0 文件;相称于一个环形。


  • write pos 和 checkpoint 的移动都是顺时针方向;
  • write pos ~ checkpoint 之间的部分(图中的赤色部分),用来记载新的更新操作;
  • check point ~ write pos 之间的部分(图中蓝色部分):待落盘的脏数据页记载;
因此,如果 write pos 追上了 checkpoint,就意味着 redo log 文件满了,这时 MySQL 不能再执行新的更新操作,也就是说 MySQL 会被壅闭
二进制日志(binlog)

作用


  • 用于复制,在主从复制中,从库利用主库上的binlog举行重放,实现主从同步。
  • 用于数据库的基于时间点的还原,即备份恢复
内容

binlog 有 3 种格式范例,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:

  • STATEMENT:每一条修改数据的 SQL 都会被记载到 binlog 中(相称于记载了逻辑操作,以是针对这种格式, binlog 可以称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
  • ROW:记载行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变革结果都会被记载,比如执行批量 update 语句,更新多少行数据就会产生多少条记载,使 binlog 文件过大,而在 STATEMENT 格式下只会记载一个 update 语句而已;
  • MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
注意:不同的日志范例在主从复制下除了有动态函数的问题,同样对对更新时间也有影响。一般来说,数据库中的update_time都会设置成ON UPDATE CURRENT_TIMESTAMP,即自动更新时间戳列。在主从复制下,
如果日志格式范例是STATEMENT,由于记载的是sql语句,在salve端是举行语句重放,那么更新时间也是重放时的时间,此时slave会偶尔间延迟的问题;
如果日志格式范例是ROW,这是记载行数据最终被修改成什么样了,这种从库的数据是与主服务器完全一致的。
什么时间产生

事件提交的时间,一次性将事件中的sql语句(一个事物大概对应多个sql语句)按照一定的格式记载到binlog中。
binlog 文件是记载了所有数据库表结构变更和表数据修改的日志,不会记载查询类的操作,比如 SELECT 和 SHOW 操作。
这里与redo log很明显的差异就是binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志。redo log 是循环写,日志空间巨细是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。
也就是说,如果不小心整个数据库的数据被删除了,只能使用 bin log 文件恢复数据。因为redo log循环写会擦除数据。
主从复制的实现

MySQL 的主从复制依赖于 binlog ,也就是记载 MySQL 上的所有变革并以二进制情势保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。
这个过程一般是异步的,也就是主库上执行事件操作的线程不会等候复制 binlog 的线程同步完成。

MySQL 集群的主从复制过程如下:

  • 写入 Binlog:MySQL 主库在收到客户端提交事件的请求之后,会先写入 binlog,再提交事件,更新存储引擎中的数据,事件提交完成后,返回给客户端“操作乐成”的相应。
  • 同步 Binlog:从库会创建一个专门的 I/O 线程,连接主库的 log dump 线程,来吸收主库的 binlog 日志,再把 binlog 信息写入 relay log 的中继日志里,再返回给主库“复制乐成”的相应。
  • 回放 Binlog:从库会创建一个用于回放 binlog 的线程,去读 relay log 中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。
什么时间刷盘

在刷盘机遇上与redolog不一样,redolog即使事件没提交,也可以每隔1秒就刷盘。但是一个事件的 binlog 是不能被拆开的,因此无论这个事件有多大(比如有很多条语句),也要保证一次性写入。如果一个事件的 binlog 被拆开的时间,在备库执行就会被当做多个事件分段自行,这样就破坏了原子性,是有问题的。
bin log日志与redo log类似,也有对应的缓存,叫 binlog cache。事件提交的时间,再把 binlog cache 写到 binlog 文件中。


  • 图中的 write,指的就是指把日志写入到 binlog 文件,但是并没有把数据持久化到磁盘,因为数据还缓存在文件系统的 page cache 里,write 的写入速度还是比较快的,因为不涉及磁盘 I/O。
  • 图中的 fsync,才是将数据持久化到磁盘的操作,这里就会涉及磁盘 I/O,以是频繁的 fsync 会导致磁盘的 I/O 升高。
MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率:

  • sync_binlog = 0 的时间,表示每次提交事件都只 write,不 fsync,后续交由操作系统决定何时将数据持久化到磁盘;
  • sync_binlog = 1 的时间,表示每次提交事件都会 write,然后立刻执行 fsync;
  • sync_binlog =N(N>1) 的时间,表示每次提交事件都 write,但累积 N 个事件后才 fsync。
显然,在MySQL中系统默认的设置是 sync_binlog = 0,也就是不做任何强制性的磁盘革新指令,这时间的性能是最好的,但是风险也是最大的。因为一旦主机发生非常重启,还没持久化到磁盘的数据就会丢失。
而当 sync_binlog 设置为 1 的时间,是最安全但是性能消耗最大的设置。因为当设置为 1 的时间,即使主机发生非常重启,最多丢失一个事件的 binlog,而已经持久化到磁盘的数据就不会有影响,不外就是对写入性能影响太大。
如果能容少量事件的 binlog 日志丢失的风险,为了提高写入的性能,一般会 sync_binlog 设置为 100~1000 中的某个数值。
两阶段提交

事件提交后,redo log 和 binlog 都要持久化到磁盘,但是这两个是独立的逻辑,大概出现半乐成的状态,这样就造成两份日志之间的逻辑不一致。如下:

  • 如果在将 redo log 刷入到磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入。那么呆板重启后,这台呆板会通过redo log恢复数据,但是这个时间binlog并没有记载该数据,后续举行呆板备份的时间,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
  • 如果在将 binlog 刷入到磁盘之后, MySQL 突然宕机了,而 redo log 还没有来得及写入。由于 redo log 还没写,崩溃恢复以后这个事件无效,而 binlog 里面记载了这条更新语句,在主从架构中,binlog 会被复制到从库,从库执行了这条更新语句,那么就与主库的值不一致性;
两阶段提交把单个事件的提交拆分成了 2 个阶段,分别是「准备(Prepare)阶段」和「提交(Commit)阶段」
详细过程


事件的提交过程有两个阶段,就是将 redo log 的写入拆成了两个步骤:prepare 和 commit,中间再穿插写入binlog,详细如下:

  • prepare 阶段:将 XID(内部 XA 事件的 ID) 写入到 redo log,同时将 redo log 对应的事件状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);
  • commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),接着调用引擎的提交事件接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘乐成,就算 redo log 的状态还是 prepare 也没有关系,一样会被认为事件已经执行乐成;
总的来说就是,事件提交后,redo log酿成prepare 阶段,再写入binlog,返回乐成后redo log 进入commit 阶段。
总结三个日志的详细流程

当优化器分析出本钱最小的执行计划后,执行器就按照执行计划开始举行更新操作。
详细更新一条记载 UPDATE t_user SET name = 'xiaolin' WHERE id = 1; 的流程如下:

  • 检查在不在buffer Pool。 执行器负责详细执行,会调用存储引擎的接口,通过主键索引树搜索获取 id = 1 这一行记载:

    • 如果 id=1 这一行所在的数据页原来就在 buffer pool 中,就直接返回给执行器更新;
    • 如果记载不在 buffer pool,将数据页从磁盘读入到 buffer pool,返回记载给执行器。

  • 检查是否已经是要更新的值。执行器得到聚簇索引记载后,会看一下更新前的记载和更新后的记载是否一样:

    • 如果一样的话就不举行后续更新流程;
    • 如果不一样的话就把更新前的记载和更新后的记载都看成参数传给 InnoDB 层,让 InnoDB 真正的执行更新记载的操作;

  • 开启事件,记载undo log,并记载修改undo log对应的redo log:开启事件, InnoDB 层更新记载前,首先要记载相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要天生一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不外在内存修改该 Undo 页面后,需要记载对应的 redo log。
  • 标记为脏页,并写入redo log:InnoDB 层开始更新记载,会先更新内存(同时标记为脏页),然后将记载写到 redo log 里面,这个时间更新就算完成了。为了淘汰磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的机遇将脏页写入到磁盘。这就是 WAL 技术,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。
  • 至此,一条记载更新完了。
  • 记载binlog:在一条更新语句执行完成后,然后开始记载该语句对应的 binlog,此时记载的 binlog 会被保存到 binlog cache,并没有革新到硬盘上的 binlog 文件,在事件提交时才会统一将该事件运行过程中的所有 binlog 革新到硬盘。
  • 事件提交,redo log和binlog刷盘。
面试题专栏

Java面试题专栏已上线,欢迎访问。

  • 如果你不知道简历怎么写,简历项目不知道怎么包装;
  • 如果简历中有些内容你不知道该不该写上去;
  • 如果有些综合性问题你不知道怎么答;
那么可以私信我,我会尽我所能帮助你。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

海哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表