前边的在《一条SQL查询在MySQL中是怎么执行的》中咱们已经介绍了执行过程当中涉及的处理模块,包括链接器、分析器、优化器、执行器、存储引擎等。今天咱们来一块儿看看一条更新语句又是怎么一个执行流程。
mysql
查询语句的一套执行流程,更新语句也会一样的走一步,下边咱们在对照上次文章中的图来简单的看一下:sql
首先,在执行语句前要先链接数据库,这是第一步中链接器的工做,前面咱们也说过,当一个表有更新的时候,跟这个表有关的查询缓存都会失效,因此咱们通常不建议使用查询缓存。
数据库
接下来,分析器会通过语法分析和词法分析,知道了这是一条更新语句后,优化器决定要使用哪个索引,而后执行器负责具体的执行,先找到这一行,而后作更新。缓存
与查询语句更新不一样的是,更新流程还涉及两个重要的日志,这个咱们在前边的文章中也有专门的介绍,有兴趣的能够找一下上周的文章《MySQL的两个日志系统》,这里就很少作介绍了。ide
下边经过一个简单的例子来分析一下更新操做的流程。优化
咱们先建立一张表,这个表有主键ID和一个整型字段c:spa
mysql> create table demo T (ID int primarty ,c int);
而后将ID=2的这一行的值加13d
mysql> update table demo set c = c + 1 where ID = 2;
接下来咱们来看看update语句的执行流程,图中浅色框表示在存储引擎中执行的,深色框表明的是执行器中执行的。日志
咱们能够看到最后的时候,写redolog的时候分了两步,prepare和commit,这就是咱们常说的“两阶段提交”。code
为何日志须要“两阶段提交”?
因为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来实现的,这个时候就可能致使线上的主从数据库不一致的状况。