乐趣区

关于mysql:MySQL三大日志binlogredologundolog详解

热衷学习,热衷生存!😄

积淀、分享、成长,让本人和别人都能有所播种!😄

一、MySQL 日志

MySQL日志次要包含谬误日志、查问日志、慢查问日志、事务日志、二进制日志几大类。其中比拟重要的就是二进制日志binlog(归档日志)、事务日志redo log(重做日志)和undo log(回滚日志)。

日志关系如下图:

二、redo log

redo log(重做日志)是 InnoDB 存储引擎独有的,它让 MySQL 有了解体复原的能力。

MySQL 实例挂了或者宕机了,重启的时候 InnoDB 存储引擎会应用 rede log 日志复原数据,保障事务的持久性和完整性。如下图:

MySQL中数据是以页为复数存储,当你查问一条记录时,硬盘会把一整页的数据加载进去,加载进去的数据叫做数据页,会放到 Buffer Pool 中。后续的查问都是先从 Buffer Pool 中找,没有找到再去硬盘加载其余的数据页直到命中,这样子能够缩小磁盘 IO 的次数,进步性能。更新数据的时候也是一样,优先去 Buffer Pool 中找

,如果存在须要更新的数据就间接更新。而后会把“在某个数据页做了什么批改”记录到重做日志缓存(redo log buffer)里,在刷盘的时候会写入 redo log 日志文件里。

如下图:

小贴士:每条 redo 记录由“表空间号 + 数据页号 + 偏移量 + 批改数据长度 + 具体批改的数据”组成。

刷盘机会

现实状况下,事务一提交就会进行刷盘操作,然而实际上是刷盘的机会是依据策略来决定的。

InnoDB存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它反对三种策略:

  • 0:设置为 0 的时候,每次提交事务时不刷盘。
  • 1:设置为 1 的时候,每次提交事务时刷盘。
  • 2:设置为 2 的时候,每次提交事务时都只把 redo log buffer 写入page cache

innodb_flush_log_at_trx_commit参数默认为 1,当事务提交的时候会调用 fsyncredo log进行刷盘,将 redo log buffer 写入 redo log 文件中。

另外,Innodb存储引擎有一个后盾线程,每隔 1 秒,就会把会 redo log buffer 中的内容写入到文件系统缓存 page cache,而后调用fsync 刷盘。

如上图,所以说一个没有提交事务的 redo log 记录,也会被刷盘。

上面是各种刷盘策略的流程图。

innodb_flush_log_at_trx_commit = 0

如上图,如果宕机了或者 MySQL 挂了可能造成 1 秒内的数据失落。

innodb_flush_log_at_trx_commit = 1

如上图,只有事务提交胜利,redo log记录就肯定在磁盘里,不会有工作数据失落。

如果执行事务的时候 MySQL 挂了或者宕机了,这部分日志失落了,然而因为事务没有提交,所以日志丢了也不会有损失。

innodb_flush_log_at_trx_commit = 2

如上图,当事务提交胜利时,redo log buffer日志会被写入 page cache,而后后盾线程会刷盘写入redo log,因为后盾线程是1 秒执行一次所以宕机或者 MySQL 挂了可能造成 1 秒内的数据失落。

日志文件组

硬盘上存储的 redo log 日志文件不止一个,而是一个 日志文件组 的模式呈现的,每个的 redo log 文件大小都是一样的。它采纳的是环形数组模式,从头开始写,写到开端回到头循环写,如下图所示:

日志文件组 中有两个重要的属性,别离是witre pos、checkpoint

  • wirte pos:是以后记录的地位,一边写一边后移。
  • checkpoint:是以后要擦除的地位,也是后盾推移。

每次刷盘 redo log 记录到 日志文件组 中,wirte log地位就会后移更新。

每次 MySQL 加载 日志文件组 复原数据时,会清空加载过的 redo log,并把checkpoint 后移更新。

write poscheckpoint 之间的还空着的局部能够用来写入新的 redo log 记录。

如果 witre pos追上 checkpoint,示意 日志文件组 满了,这时候不能再写入新的 redo log 记录,MySQL得停下来,清空一些记录,把 checkpoint 举荐一下。

redo log 小结

redo log的作用和它的刷盘机会、存储模式。

能够思考一个问题:只有每次把批改后的数据页间接刷盘不就好了,为什么还要用 redo log 刷盘?不都是刷盘吗?有什么区别?

实际上,数据页大小是 16KB,刷盘比拟耗时,可能就批改了数据页的几byte 数据,没有必要把整页的数据刷盘。而且数据页刷盘都是随机写,因为一个数据页对应的地位可能是在硬盘文件的随机地位,所以性能很差。

如果是写redo log,一行记录就占了几十byte,只有蕴含了表空间号、数据页号、磁盘文件偏移量、批改值,再加上是程序写,所以刷盘效率很高。

所以用 redo log 模式记录批改内容,性能会远远超过刷数据页的形式,这也让数据库的并发能力更强。

三、binlog

redo log是物理日志,记录的是“在某个数据页做了什么批改”,属于 Innodb 存储引擎。

binlog 日志是逻辑日志,记录内容是语句的原始逻辑,属于 MySQL Server 层。所有的存储引擎只有产生了数据更新,都会产生 binlog 日志。

binlog日志的作用

能够说 MySQL 数据库的 数据备份、主备、主主、住从 都离不开 binlog,须要依赖binlog 来同步数据,保证数据一致性。

binlog会记录所有波及更新数据的逻辑规定,并且按程序写。

记录格局

binlog日志有三种格局,能够通过 binlog_format 参数设置,有以下三种:

  • statement
  • row
  • mixed

设置 statement 记录的内容是 SQL 语句原文,比方执行一条update T set update_time = now() where id = 1,记录内容如下:

同步数据时,会执行记录的 SQL 语句,然而有个问题 update_time = now() 这里会获取到以后零碎问题,间接执行会导致与原库数据不统一。

为了解决这种问题,咱们须要将 binlog_format 设置成 row,记录的不再是简略的SQL 语句了,还蕴含了操作的具体数据,记录内容如下:

row格局记录的内容看不到详细信息,通过 mysqlbinlog 工具解析进去。

update_time = now()变成了具体的工夫,条件前面的 @1、@2 都是该行数据第 1 个~2 个字段的原始值(假如这张表只有 2 个字段)。

设置成 row 带来的益处就是同步数据的一致性,通常状况都设置成 row,这样能够为数据库的复原与同步带来更好的可靠性。然而这种格局须要大量的容量来记录,比拟占用空间,复原与同步时会更耗费IO 资源,影响执行速度。

所以又有了一种折中计划,设置为mixed,记录的内容是前两者的混合。

MySQL会判断这条 SQL 语句是否会引起数据不统一,如果是就用 row 格局,否则就用 statement 格局。

写入机制

binlog的写入机会为事务执行过程中,先把日志写到 binlog cache,事务提交的时候再把binlog cache 写到 binlog 文件中(理论先会写入 page cache,而后再由fsync 写入 binlog 文件)。

因为一个事务的 binlog 不能被拆开,无论这个事务多大,也要确保一次性写入,所以零碎会给每个线程调配一块内存作为 binlog cache。能够通过binlog_cache_size 参数管制单线程 binlog_cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘。

binlog日志刷盘流程如下:

  • 上图的write,是指把日志写入到文件系统的page cache,并没有把数据长久化硬盘,所以速度比拟快。
  • 上图的 fsync 才是将数据库长久化到硬盘的操作。

writefsync 的机会能够由参数 sync_binlog 管制,能够配置成0、1、N(N>1)

  • 设置成 0 时:示意每次提交事务都只会write,由零碎自行判断什么时候执行fsync
  • 设置成 1 时:示意每次提交事务都会执行 fsync,就和redo log 日志刷盘流程一样。
  • 设置成 N 时:示意每次提交事务都会 write,然而积攒N 个事务后才fsync

设置为 0 时如下图:

从上图可知,sync_bilog = 0设置成 0,只把日志写入page cache 尽管性能失去了进步,然而事务提交了 fsync 的时候宕机了,可能造成 binlog 日志的失落。

设置为 2 时如下图:

在呈现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比拟大的值,能够晋升性能。

同样的,如果机器宕机,会失落最近 N 个事务的 binlog 日志。

两阶段提交

redo log(重做日志)让 InnoDB 存储引擎有了解体复原的能力。

binlog(归档日志)保障了 MySQL 集群架构数据的一致性。

尽管它们都属于长久化的保障,然而侧重点不一样。

在执行更新语句过程,会记录 redo logbinlog两块日志,以根本的事务为单位,redo log在事务执行过程中能够一直写入,而 binlog 日志只有在提交事务的时候才会写入,所以它们写入的机会不一样。

思考一个问题,如果 redo log 和 binlog 两份日志之间的逻辑不一样,会呈现什么问题呢?MySQL 是怎么解决这个问题的呢?

比方有这样一个场景,假如有这么一条语句 update T set c = 1 where id = 2(c 原值为 0),如果执行过程中写完redo log 日志后,在写入 binlog 的时候产生了异样,会呈现什么状况呢?

如下图:

因为 binlog 日志没写完就异样,这个时候 binlog 日志外面没有对应的批改记录,之后应用 binlog 同步的数据的时候就会少这一次的更新,这一行数据 c = 0,而原库应用redo log 日志复原,这一行数据c = 1 ,最终数据不统一。如下图:

为了解决两份日志之间的逻辑不统一的问题,InnoDB存储引擎应用 两阶段提交 计划。

redo log 日志的写入拆分成两个步骤 preparecommit,如下图:

应用 两阶段提交 后,写入 binlog 时产生异样也没关系,因为 MySQL 依据 redo log 日志复原数据时,发现 redo log 日志处于 prepare 阶段,并且没有对应 binlog 日志(依据事务 id 对应),所以就会回滚事务。

再想一个场景,redo lgo设置 commit 阶段产生异样,事务会不会回滚呢?

并不会回滚事务,尽管 redo log 是处于 prepare 阶段,然而存在对应的事务 binlog 日志,所以 MySQL 认为是残缺的,所以不会回滚事务。

undo log

想要保障事务的原子性,就须要在产生异样时,对曾经执行的操作进行回滚,在 MySQL 中复原机制是通过undo log(回滚日志)实现的,所有事务进行的批改都会先被记录到这个回滚日志,而后再执行其余相干的操作。如果执行过程中遇到异样的话,咱们间接利用回滚日志中的信息将数据回滚到批改之前的样子。并且,回滚日志会先于数据长久化到磁盘上。这样就保障了即便遇到数据库忽然宕机等状况,当用户再次启动数据库的时候,数据库还可能通过查问回滚日志来回滚将之前未实现的事务。

另外,MVCC的实现依赖:暗藏字段、Read Viewundo log。在底层实现中,InnoDB通过数据行的 DB_TRX_IDRead View来判断数据的可见性,如不可见,则通过数据行 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事物里,用户只能看到该事务创立 Read View 之前曾经提交的批改和该事务自身做的批改。

总结

MySQL InnoDB引擎应用 redo log 日志保障事务的持久性,应用 undo log 日志保障事务的原子性。

MySQL数据库的 数据备份、主备、主主、主从 离不开 binlog,须要依赖binlog 来同步数据,保证数据的一致性。

参考:https://javaguide.cn/database…

退出移动版