MySQL实战-2 日志系统:一条更新语句是如何执行的?

2018-12-06  本文已影响0人  allen成

前一节已经介绍了执行过程中涉及的处理模块。一条查询语句的执行过程是一般经过连接器、分析器、优化器、执行器等功能模块,最后到达存储引擎。
那么,一条更新语句的执行流程又是怎么样的呢?
也许你可能经常听到DBA同事说,MySQL可以恢复到半个月内任意一秒的状态,惊叹的同时,你是不是也会好奇,这是怎么样做到的呢?
我们还是从一个表的一条更新语句说起,下面是这个表的创建语句,这个表有一个主键ID和一个整型字段C:

mysql> create table T(ID int primary key, c int);

如果要将这ID=2这一行的值增加1,SQL语句就会这么写:

mysql> update T set c=c+1 where ID=2;

前面我有跟你介绍过SQL语句基本的执行链路。

MySQL 逻辑架构图
你想要执行语句,就需要先连接数据库,这是连接器的工作。
前面我们说过,在一个表更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表T上所有缓存结果都清空。这也就是我们一般不建议使用查询缓存的原因。
接下来,分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用ID这个索引。然后,执行器负责具体执行,找到这一行,然后更新。
与查询流程不一样的是,更新流程还涉及两个重要的日志模块,它们正是我们今天要讨论的猪脚:redo log(重做日志)和binlog(归档日志)。如果接触MySQL,那这两个词肯定是绕不过的。
重要的日志模块:redo log
MySQL如果没一次更新都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后在更新,整个过程IO成本、查找成本很高。为了解决这个成本高的问题,MySQL就采用了WAL技术(Write-ahead logging),它的关键点就是先写日志,再写磁盘,这样就可以防止忙碌的时候,造成IO输出过高。
具体来说,当有一条记录需要更新的时候,InnoDB引擎就会把记录写到redo log 里面,并更新到内存,这个时候更新就完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。
如果系统在空闲的时候,可以等到写redo log日志后,在写到内存磁盘。如果系统比较繁忙的时候,这时redo log 日志也写满的时候,这时只能暂停redo log 日志的写入,将redo log 中的部分记录更新到内存磁盘中。然后清除已更新到内存磁盘中的记录,腾出空间为新记录。
与此类似,InnoDB的redo log 是固定大小的,比如可以配置为一组4个文件夹。每个文件的大小为1GB,那么这个redo log 日志文件的总共可以记录4GB的文件。
从头开始写,写到末尾就又回到开头循环写,如下图所示。
redo log 写日志流程
write pos 是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是redo log 日志文件中空着的部分,还可以用来记录新的操作。如果write pos 追上 checkpoint ,表示 redo log 已经满了,这时不能再执行新的更新,得停下来先擦除一些记录,把checkpoint推进一下。
有了redo log , InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe
要理解crash-safe这个概念,例如赊账将记录记录在了粉板上或者写在了账本上,之后即便是掌柜忘记了,比如突然停业几天,恢复生意后依然可以通过账本和粉板上的数据明确赊账账目。
重要的日志模块:binlog
MySQL整体看来,其实就有两块,一块是Server层,它主要做的是MySQL功能层面的事情;还有一层就是引擎层,负责存储相关的具体事宜。redo log 日志就是InnoDB引擎特有的日志,而Server层也有自己的日志,称为binlog(归档日志) 。

我想你肯定会问,为什么会有两份日志呢?
因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。
这两种日志有以下三点不同。

1 prepare阶段 2 写binlog 3 commit
当在2之前崩溃时
重启恢复:后发现没有commit,回滚。备份恢复:没有binlog 。 >>>一致
当在3之前崩溃
重启恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit。备份:有binlog. >>一致

binlog是可以关的,你如果有权限,可以set sql_log_bin=0关掉本线程的binlog日志。 所以只依赖binlog来恢复就靠不住。

崩溃恢复过程的话,可以接受“redolog prepare 并且binlog完整” 的情况
Crash-safe是崩溃恢复,就是原地满血复活;binlog恢复是制造一个影分身(副本)

上一篇下一篇

猜你喜欢

热点阅读