一条更新SQL在MySQL数据库中是如何执行的

前边的在《一条SQL查询在MySQL中是怎么执行的》中我们已经介绍了执行过程中涉及的处理模块,包括连接器、分析器、优化器、执行器、存储引擎等。今天我们来一起看看一条更新语句又是怎么一个执行流程。

查询语句的一套执行流程,更新语句也会同样的走一步,下边我们在对照上次文章中的图来简单的看一下:

首先,在执行语句前要先连接数据库,这是第一步中连接器的工作,前面我们也说过,当一个表有更新的时候,跟这个表有关的查询缓存都会失效,所以我们一般不建议十月查询缓存。

接下来,分析器会经过语法分析和词法分析,知道了这是一条更新语句后,优化器决定要使用哪一个索引,然后执行器负责具体的执行,先找到这一行,然后做更新。

与查询语句更新不同的是,更新流程还涉及两个重要的日志,这个我们在前边的文章中也有专门的介绍,有兴趣的可以找一下上周的文章《MySQL的两个日志系统》,这里就不多做介绍了。

下边通过一个简单的例子来分析一下更新操作的流程。

我们先创建一张表,这个表有主键ID和一个整型字段c:

mysql> create table demo T (ID int primarty ,c int);

然后将ID=2的这一行的值加1

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

接下来我们来看看update语句的执行流程,图中浅色框表示在存储引擎中执行的,神色框代表的是执行器中执行的。

我们可以看到最后的时候,写redolog的时候分了两步,prepare和commit,这就是我们常说的“两阶段提交”。

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

由于redo log和binlog分别是存储引擎和执行器的日志,是两个独立的逻辑,如果不用两阶段提交,无论先提交哪个后提交哪个都会存在一些问题。我们这里也借助上边的例子看一下,假设当前ID=2的这一行值为0 ,在update的过程中写完了第一个日志后,第二个日志还没写期间发生了crash,会怎么样?

  • 先写redolog后写binlog。假设redolog写完,binlog还没写完,MySQL进程异常重启了。我们知道,redolog写完以后,系统即使崩溃了,也可以将数据恢复,所以在MySQL重启后,这一行回被恢复成1。由于binlog没写完就crash,这时候binlog里面是没有这个语句的,因此之后备份日志的的时候,存起来的binlog日志也没有这一条语句。当我们需要通过binlog来恢复数据的时候,由于binlog丢失了这条语句,恢复出来的这一行的值就是0,与原库的值不一样啦。
  • 先写binlog后写redo log。如果写完buglog之后,redo log还没写完的时候发生 crash,如果这个时候数据库奔溃了,恢复以后这个事务无效,所以这一行的值还是0,但是binlog里已经记载了这条更新语句的日志,在以后需要用binlog来恢复数据的时候,就会多了一个事务出来,执行这条更新语句,将值从0更新成1,与原库中的0就不同了。

我们可以看到如果不使用“两阶段提交",那么数据库的状态就会和用日志恢复出来的库不一致。虽然平时用日志恢复数据的概率比较低,但是用日志最多的还是扩容的时候,用全量备份和binlog来实现的,这个时候就可能导致线上的主从数据库不一致的情况。
如果喜欢文章不妨点个赞吧,也可以关注微信搜索故里学Java,第一时间获取更新的干活文章

你可能感兴趣的:(一条更新SQL在MySQL数据库中是如何执行的)