MySQL基础架构 --- 一条语句是怎么执行的?

​ 对于一个很简单的表,里面只有一个字段ID,执行下列语句时

1
select * from T where ID = 10;

​ 我们对该语句的执行过程进行,一个拆解。

MySQL的基本架构图

1633185245582

  • 连接器:管理数据库连接
  • 分析器:类似编译原理,进行一个SQL语法的分析,判断是否符合语义
  • 优化器:进行索引的选择
  • 执行器:引擎操作器,拿到结果
  • 缓存: 保存之前的页,如果有就直接返回(一般mysql很少开启缓存)
  • 引擎层:如何存放,并且组织数据

日志系统 一条更新语句如何完成(两阶段提交)

​ 之前学习select流程的时候,一条查询语句的执行顺序是客户端连接器分析器,优化器执行器,引擎

​ update语句,跟select语句有点类似,但是又有很多区别(涉及到日志的两阶段提交)

update语句执行流程

1
2
update T set c = c + 1 
where ID = 10;

​ 对于这条更新语句,select所走的所有流程,update都会走一遍,因为只有拿出该页才能对record进行一个update。

​ 但是与查询流程不同的是,update还涉及到两个很重要的模块**redo log(重做日志)和binlog(归档日志)**。

redo log(引擎层的日志)

​ 在Innodb中,redo log可以理解为一个可以循环使用的文件。为了循环使用,必须有两个指针。分别为write_pos 和 check_pos

  • write_pos:新的一条日志记录,从哪里开始记录
  • check_pos:表示,循环开始的地方。如果write_pos == check_pos,代表当前文件已经写满,必须fresh当前的重做日志了

1633185258741

作用

  • 让数据库有了对事务进行重做的能力,能把还没来得及归档的数据,通过日志重新跑一编。

binlog(server层日志)

​ MySQL 整体来看,其实就有两块:

  • 一块是 Server 层,它主要做的是 MySQL 功能层面的事情;
  • 还有一块是引擎层,负责存储相关的具体事宜。

​ binlog就是server层独有的日志,记录的是对某个记录的逻辑上的操作(比如,给ID=2这条记录的C字段+1)

为什么server层也要有一个日志?

  • 因为,innodb不是mysql原生的存储引擎,而是作为一个插件来进行补充的。
  • 因此server原生的MYISAM引擎,理所应当也要有一个日志binlog

两者区别

  • redo log只属于innoDB引擎;binlog是所有MySQL中server层的日志

  • redo log物理日志,每条记录表示在某个数据页进行了什么操作

  • binlog是逻辑日志,每条记录的逻辑变化(给某个字段 + n)

  • redo log循环写,binlog顺序写

update的两阶段提交

​ 有了两个日志的概念,下面讲讲update语句的执行流程

  • 通过执行器,取出对应的数据页
  • 并且给对应的记录进行操作,再给到innoDB引擎
  • 引擎拿到改写的页后,刷盘,并且把该操作写入redo log中
  • 并把对应的事务ID标记为,prepare可以提交状态

  • prepare之后,告知执行器,刷盘完成了。
  • server层中,生成该操作的binlog,并且写入磁盘,再告知引擎
  • 引擎把prepare状态改为commit

​ 以上就是两阶段提交的全部过程。

如何让数据库恢复到半个月内的任意一秒?

场景:

1
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据

操作:

  • 找到最近一次的全量备份(read-view)
  • 然后重跑全量备份 到 昨天中午十二点的删表操作的binlog

为什么要有两阶段提交?

  • 因为有了两阶段提交,数据库通过这两个日志进行crash-safe之后,一定能保证恢复的数据是自洽的。

  • 因为恢复数据库的时候,需要重跑binlog。我们需要拿出binlog的每条记录跟redo log进行比对,看看是否commit,如果commit就执行,不然就放弃

如果没有两阶段提交

(1)先提交redo log,再提交binlog

1
2
3
4
1.redo log中记录了一条,set C=C+1 WHERE ID = 10的记录,(C=0)
2.写入binlog中途宕机了,当值binlog没有该记录
3.crash的时候,我们是希望C=1,因为redo log已经记录了
4.但是重跑binlog的时候,缺失了该条记录,因此最后C=0,显然跟想要的结果不一致

(2)先提交binlog,再提交redo log

1
2
3
4
1.binlog写完,redo log没写完就宕机了
2.意味着,其实db是不承认这条记录的
3.因此跑binlog的时候,该条在redo log缺失的记录仍旧会被执行。
4.最后造成了,恢复的数据库 > 想要的数据库。

QA

1.为什么binlog没有crash-safe功能

1
2
3
4
5
6
7
8
9
10
11
1.根本原因是 redo-log 是循环写。因为是循环写,带来了刷盘机制
2.binlog 记录的是给"id = 2,这行某个极端+1"
3.因为binlog没有刷盘机制,因此没有一个标记,让binlog判断哪些数据已经刷盘了

举个栗子
binlog中有两条数据,init c = 0
(1)给ID=2,这行c字段加1
(2)给ID=2,这行c字段加1

假设,写入(1)的时候,刷盘了。那么redo log只要重做(2)即可了。
如果没有redo log,那么binlog无法判断哪条数据已经刷盘了,如果全量重跑一遍数据的话,很可能会出错。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!