系列文章

  • 原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》
  • 毕生挚友redo log、binlog《死磕MySQL系列 二》

上期依据一条查问语句查问流程剖析MySQL的整体架构。同样,本期也应用一条查问SQL语句来做引子。能够必定的是,查问语句执行的流程更新语句同样也会执行。

因而本期的着重点就不在MySQL架构图上,文章题目也给出了大家重点,就是要理解redo log、binlog。

一、redo log

第一步,创立一个表 user,主键是 id,上面是创立语句。

CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `age` tinyint(4) NOT NULL, `time` int(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

插入一条数据

insert into user (`name`,`age`,`time`) values ("咔咔","25",unix_timestamp(now()))

若要将插入的这条数据的age改为26,则须要执行语句

update user set age = 26 where id = 1;

第一期文章中提到一条查问语句的执行流程,该流程与更新语句雷同。这里将那幅图拿过去在相熟一下。

每个模块的性能能够回到第一期文章去查看。

在MySQL8.0中redo log、binlog日志文件都位于/var/lib/mysql此目录下,如图

文件名为ib_logfile的是重做日志,undo结尾的就是回滚日志,对于回滚日志前期进行具体的探讨。

redo log(重做日志)是实现事务持久性必备因素,当一个事务提交后,并非间接批改数据库的数据,而是首先保障在 redo log中记录相干的操作。

Innodb存储引擎中的redo log大小是固的,上图显示配置了一组两个文件,每个文件大小默认为48M,应用innodb_log_file_size参数来管制单个文件大小,在MySQL5.6.8以及之后版本都默认为48M。

而后redo log能够记录48M的操作,redo log是一个闭环的循环写。所设定的文件个数和文件大小不再减少。

write pos将记录以后地位,同时向后挪动,在ib-log-file-3文件开端后,而后返回ib-logfilg-0文件开始写。

check point记录的是以后擦除的地位,要使文件循环写入,必须一边擦除。分明数据的前提是要将记录更新到数据文件。

下面的绿色局部就是可写的局部,假如如果 writepos追上了 checkpoint,那该怎么办?

你必须了解write pos的推动是因为在执行更新操作,这样就不能再执行更新操作,直到记录更新到数据文件,而后check point进行擦除后才能够继续执行更新操作。

对于innodb_log_file_size的设置也是有一些计算规定的,上面将为你介绍。

若innodb_log_file_size设置太小,将导致redo log文件频繁切换,频繁的触发数据库的检查点(check point),导致记录更新到数据文件的次数减少,从而影响IO性能。

同样,如果有一个大的事务,并且所有 redo log日志都已写满,然而还没有实现,将导致日志无奈切换,从而导致 MySQL间接堵死。

innodb_log_file_size设置太大,尽管极大地提高了 IO性能,然而在 MySQL重启或宕机时,复原工夫会因为 redo log文件过大而缩短。而这种复原工夫通常是无法控制的。

在设置正当的redo log大小和数量后,Innodb可能保障,即便数据库产生异样重启,以前提交的记录也不会失落,这一点也称为crash-safe。

在这里,对crash-safe的了解先不提及它是什么,前面的文章会让你明确。

二、如何依据我的项目状况设置innodb_log_file_size

对于参数innodb_log_files_in_group设置3~4个就够用了,不必进行优化。

着重探讨innodb_log_file_size的大小设置或优化设置。

在 MySQL8.0之前,通常是计算在一段时间内生成的事务日志(redo log)大小,而 MySQL日志文件最小应承载一小时的业务日志量。

此处的一段时间必须视本人的业务状况而定,外界有用1分钟的日志量也有1小时的日志量来计算。

首先看一下 MySQL客户端的一个命令 pager,在 MySQL日常操作中,通过设置 pager的显示方式,能够大大提高工作效率。

目前,要查看 sequence在一分钟之内的值,您就能够执行 pager grep sequence,它对 mysql> show engine innodb status\ G select sleep (60); show engine innodbstatus\ G;返回的后果。

禁止 pager设置执行 nopager,如果不执行该命令,则只有等到下一次重新启动该命令才会生效。

此处咔咔是在虚拟机上做的操作,能够看到一分钟内是没有任何操作,所以值前后雷同,你能够在测试服务器做测试。

这样计算出来的 select (后边数据-后面的数据)/1024/1024*60 asMB_per_hour;值是一个小时后 redo log的大小

然而用这种办法计算肯定是不适合的,在一分钟内业务忙碌或者业务闲暇工夫计算出的值都会产生较大误差。

适合的办法是在一天中确定几个工夫点,用一个脚本定时执行,而后记录相应的值,再取平均值,计算出的误差将减至最小。

什么是 sequece?
当每个 binlog生成时,该值从1开始,而后递增,每减少一个事务, sequenumber就加上1。

二、binlog

您能够从总体上理解到 MySQL架构分为两层,一个是 server层,另一个是存储引擎层。

server层当然是负责性能方面的,而存储引擎层则负责解决与存储相干的操作。

而且下面提到的redo log是Innodb存储引擎层特有的,其它存储引擎是不具备的,而server层也有本人的日志记录,就是将要聊到的binlog。

redo log和binlog的区别

redo log是Innodb引擎特有的,而binlog是MySQLserver层特有的,所有引擎都能够应用。

redo log是物理日志,它记录的是一条更新操作所做的批改,binlog是逻辑日志,记录的是一条更新语句执行逻辑

redo log是循环写的,并且空间是固定的,比方下面配置4个1GB的redo log文件,binlog是追加写的,这个文件写完了,换下一个文件,不会笼罩以前的日志。这也就是你常常看到只有你有残缺的binlog文件就能够给你复原到你想要的数据。

MySQL为什么会有俩份日志呢?

在没有Innodb存储引擎之前,MySQL默认存储引擎是MyIsam,但MyIsam是没有重启恢复能力的,binlog日志也仅用于归档。

Innodb是另一家公司以插件的模式引入到Mysql,既然binlog没有重启复原的能力,那么我就应用redo log来实现重启复原的性能。

这就导致了当你应用Innodb存储引擎时会写俩份日志。

三、什么是两阶段提交

对redo log、binlog有了肯定的意识后再来看看一条更新语句的执行流程。

update user set age = age + 1 where id = 1;

  • 执行器先到引擎层找到id = 1这一行,因为ID是主键,所以会在主键索引树找到这一行。如果ID=2这一行所在的数据页原本就在内存中,就间接返回给执行器。否则,须要先从磁盘中读入内存,而后再返回。
  • 执行器拿到存储引擎返回id = 2后果后,给age加上1,原来是25,当初就是26,在调用引擎接口写入这行新数据。
  • 引擎将这行数据先更新到内存中,同时将这个更新操作记录到redo log中,此时redo log处于prepare状态。而后告知执行器执行实现了,随时能够提交事务。
  • 接着执行器生成这个操作的binlog,并把binlog写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交commit状态,更新实现。

到这里你应该就清晰了,一条更新SQL会先写redo log再写binlog,这也就是题目为什么叫毕生挚友redo log、binlog

四、为什么须要两阶段提交

是为了让redo log跟binlog两份日志之间的逻辑统一,看上面俩种状况。

先写redo log后写binlog

  • 更新语句为age = age +1
  • 将数据写入redo log,MySQL过程异样重启
  • 此时binlog还没有开始写
  • 零碎重启后进行数据恢复此时的值为26
  • 须要搭建从库时须要拿binlog进行复原数据,但此时age = age +1 这行的操作是没有记录到binlog的
  • 那么此时的从库就会少这一次的更新,复原进去的age仍然是25,造成于主库数据不统一。

先写binlog后写redo log

  • 更新语句为age = age +1
  • 将数据写入binlog,MySQL异样重启
  • 此时redo log 还没写
  • MySQL零碎重启,这个更新操作是对于redo log是不存在的,所以重启后的值仍然是25
  • 但binlog 中的值已将是26了
  • 须要搭建从库时,从库的值是26,主库的值是25,造成主从数据不统一

所以说,如果不应用两阶段提交,那么原库和用它的binlog日志复原进去的库数据是不统一的。

五、《孔乙己》让你明确redo log是什么

来看一个初中九年级语文课文中《孔乙己》这篇文章,就算不记得内容,题目总记得哈!

这个案例也是看丁老师文章中提到的,为什么丁老能够灵便的应用这个案例来讲redo log而咱们想不到呢?

其本质起因是对知识点没有了解透彻,应用生存案例来解释技术是让人最容易了解并不难忘记的。

《孔乙己》中的主人公就叫他酒店掌柜,掌柜的有俩件法宝让比其余老板工作效率高很多。一个是小黑板另一个是账本。

试想一下如果有客人要赊账,是间接写到黑板效率高,还是翻稀稀拉拉的账原本的快呢?

掌柜必定会抉择先记录到黑板上,等人少或者不忙时再把黑板的记录写到账本中。

反之老板没有黑板的话,只能在稀稀拉拉的账本中先找到赊账人的名字,如果之前有赊账记录追加,找了一遍发现没有才进行新增。

这个过程不仅繁琐而且效率低的让人难以承受,如果酒店客人多老板是记录不过去的。

同样,在MySQL中也会存在这个问题,每次执行更新语句都须要先找到那条记录,而后再更新,整个过程IO老本、查找老本都很高。所以MySQL也利用了酒店掌柜的智慧应用黑板来晋升执行效率。

画一幅图让大家能更好的了解掌柜、黑板、在MySQL中的对应关系。

六、redo log参数详解

事务的持久性就是通过重做日志来实现的。

当提交事务之后,并不是间接批改数据库的数据的,而是先保障将相干的操作记录到redo日志中。

数据库会依据相应的机制将内存的中的脏页数据刷新到磁盘中。

上图是一个简略的重做日志写入流程。

在上图中提到俩个生疏概念,Buffer pool、redo log buffer,这个俩个都是Innodb存储引擎的内存区域的一部分。

而redo log file是位于磁盘地位。

也就说当有DML(insert、update、delete)操作时,数据会先写入Buffer pool,而后在写到重做日志缓冲区。

重做日志缓冲区会依据刷盘机制来进行写入重做日志中。

这个机制的设置参数为innodb_flush_log_at_trx_commit ,参数别离为0,1,2

上图即为重做日志的写入策略。

  • 当这个参数的值为0的时,提交事务之后,会把数据寄存到redo log buffer中,而后每秒将数据写进磁盘文件
  • 当这个参数的值为1的时,提交事务之后,就必须把redo log buffer从内存刷入到磁盘文件里去,只有事务提交胜利,那么redo log就必然在磁盘里了。
  • 当这个参数的值为2的状况,提交事务之后,把redo log buffer日志写入磁盘文件对应的os cache缓存里去,而不是间接进入磁盘文件,1秒后才会把os cache里的数据写入到磁盘文件里去。

服务器异样进行对事务如何应答(事务写入过程)

  • 当参数为0时,前一秒的日志都保留在日志缓冲区,也就是内存上,如果机器宕掉,可能失落1秒的事务数据。
  • 当参数为1时,数据库对IO的要求就十分高了,如果底层的硬件提供的IOPS比拟差,那么MySQL数据库的并发很快就会因为硬件IO的问题而无奈晋升。
  • 当参数为2时,数据是间接写进了os cache缓存,这部分属于操作系统局部,如果操作系统局部损坏或者断电的状况会失落1秒内的事务数据,这种策略绝对于第一种就平安了很多,并且对IO要求也没有那么高。

小结

对于性能:0>2>1

对于平安:1>2>0

依据以上论断,所以说在MySQL数据库中,刷盘策略默认值为1,保障事务提交之后,数据相对不会失落。

保持学习、保持写作、保持分享是咔咔从业以来所秉持的信念。愿文章在偌大的互联网上能给你带来一点帮忙,我是咔咔,下期见。