0
点赞
收藏
分享

微信扫一扫

第二章 从更新角度看MySQL 执行流程


第二章 从更新角度看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;

执行流程

  1. 连接器进行连接数据库的工作
  2. 分析器会通过词法和语法解析知道这是一条更新语句
  3. 优化器决定使用ID这个唯一索引
  4. 执行器调用引擎负责具体执行

更新流程涉及两个重要的日志模块

  • ​redo log​​(重做日志)
  • ​binlog​​(归档日志)

重要的日志模块:redo log

什么是 redo log?

  • ​redo log​​​ 是一种 WAL 技术应用,英文全称为​​write-aheading-log​
  • 即首先将记录写入到日志里,当数据库空闲时,将记录写入磁盘
  • 为何有会采用这种计数呢,因为执行一条SQL更新语句会去写磁盘,然后磁盘会去查找对应的行,再进行更新,整个过程无论是IO操作,还是查找操作消耗都很大
  • 因此 MySQL 存储引擎​​innodb​​​ 采用将数据先写入到​​redo log​​,然后更新内存,这样就算完成一条更新语句,然后在适当的时候再将数据更新到磁盘中。
  • 同时该技术能够结果​​crash-safe​​​ 问题,当系统突然崩溃时,可以通过​​redo log​​ 恢复崩溃前的状态

先写日志,再写磁盘

  • 需要注意的是:“先写日志” 也是先写磁盘,只是写日志是顺序写盘,速度很快
  • 先写 redo log 到 log buffer ,具体内容就是针对哪个表空间的哪些页面做了哪些修改
  • 然后 log buffer 中的日志内容会在某些时候写到 redo log 日志文件中,比如事务提交时

为什么写 redo log 日志会比刷新内存中的数据页到磁盘快?

  • 是因为服务器在启动时就已经给 redo log 日志文件分配好了一块物理上连续的磁盘空间,每次写 redo log 日志都是往文件中追加写,并没有寻址的过程
  • 而修改过的数据页要刷新到磁盘的话,可能对应的磁盘空间并不是物理连续的,找起来费劲

总结

  • InnoDB引擎先把记录写到 redo log 中,redo log 在哪,它也是在磁盘上,这也是一个写磁盘的过程
  • 但是与更新过程不一样的是,更新过程是在磁盘上​​随机IO​​,费时
  • 而写 redo log 是在磁盘上​​顺序IO​​。效率要高

InnoDB 的 redo log 是固定大小的

  • 比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么总共就可以记录 4GB 的操作
  • 从头开始写,像循环队列一样,写到末尾又回到开头循环写

第二章 从更新角度看MySQL 执行流程_mysql

  • ​write pos​​ 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头
  • ​checkpoint​​ 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件
  • 擦除记录之前要把记录更新到数据文件,也就是把记录更新到磁盘文件上,这个事情是在系统比较空闲的时候去做的
  • ​write pos​​​ 和​​check point​​​ 之间就是还能写数据的部分,如果追上​​checkpoint​​​了,就要先刷写一次,把​​checkpoint​​ 往后推

​crash-safe​

  • redo log 是 InnoDB 引擎所特有的
  • 所以如果我们在使用 InnoDB 引擎创建表时,如果数据库发生异常重启,之前提交的记录都不会丢失
  • InnoDB正因为有了 redo log (重做日志),才有了 crash-safe 的能力(即使mysql服务宕机,也不会丢失数据的能力)

重要的日志模块:binlog

redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 ​​binlog(归档日志)​

为什么会有两份日志呢?

  • 因为最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。
  • 而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。

​binlog​​​ 为什么没有 ​​crash_safe​​ 的能力呢?

  • 写入方式的问题,binlog 是追加写,crash 时不能判定 binlog 中哪些内容是已经写入到磁盘,哪些还没被写入
  • 而 redolog 是循环写,从 check point 到 write pos 间的内容都是未写入到磁盘的
  • 假如现在重新设计 MySQL,只用一个 binlog 实现cash_safe,只不过 binlog 中也要加入​​check point​​​,数据库故障重启后,binlog​​check point​​之后的 sql 都重放一遍。但是这样做让 binlog 耦合的功能太多。

redo log 和 binlog 有什么区别?

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • redo log 是物理日志,记录的是 “在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

执行器 和 InnoDB 引擎在执行这个 ​​update​​​ 语句时的​​内部流程​

  • 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  • 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  • 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  • 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  • 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

​update​​ 语句的执行流程图

  • 图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。

第二章 从更新角度看MySQL 执行流程_mysql_02

  • 对数据操作的行为(加减),由「执行器」完成;
  • 对数据读取行为,由「引擎」完成。

将 ​​redo log​​​ 的写入拆成了两个步骤:​​prepare​​​ 和 ​​commit​​,这就是"两阶段提交"

  • ​redo log​​​ 等待​​binlog​​​ 写入完成后,由​​prepare​​​ 变为​​commit​​ 提交状态
  • 两阶段提交都是为了保障一致性,这里是保障​​redo log​​​ 和​​binlog​​ 的一致性,最终是保障db数据的一致性

为什么日志需要 “​​两阶段提交​​” ?

  • 如果不使用两阶段提交,先写 redo log 后写 binlog,在写完 redo log 后,写 binlog 的时候发生 crash。
  • 数据库恢复回来后,数据没丢失,因为能根据 redo log 恢复回来,但是这个操作却少了一个 binlog。
  • 而在进行数据库备份的时候使用的是 binlog,所以备份的数据里面就丢失了这次更改,以后在使用这个备份恢复的时候,自然恢复回来的数据就不对。

总结

  • redo log 用于保证 crash-safe 能力。​​innodb_flush_log_at_trx_commit​​ 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
  • sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。
  • redo log 记录的是磁盘上数据的物理变化,binlog 记录的是当时所执行的高级编程语言 (sql语句)


举报

相关推荐

从开发角度看项目流程

TypeScript 第二章

第二章 翻译

第二章 资产

第二章 IDLE

第二章-表格

0 条评论