热衷学习,热衷生存!

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

一、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...