写在前面
以下内容基于InnoDB存储引擎分析。
在01基础架构:一条SQL查询语句是如何执行的 一文中我们分析了mysql的基础架构,了解了一个sql语句执行的简单流程,为了方便回忆,再来贴下架构图:
如果是我们执行了如下的更新语句mysql> update T set c=c+1 where ID=2; ,也是经过上述的各种组件处理吗?答案是肯定的,但是因为是更新数据,肯定涉及到新数据的写入,所以具体的流程细节上又会有所不同,主要的不同之处就在于执行器这里,接下来我们就一起来看下,具体参考1:更新语句在执行器做了什么? 。
1:更新语句在执行器做了什么?
当更新语句比如update T set c=c+1 where ID=2; 执行到执行器,执行器因为需要完成数据更新任务,所以首先需要调用存储引擎的获取数据接口获取到ID=2的数据 ,然后执行c=c+1 ,然后调用存储引擎的更新接口将ID=2的c数据更新为c+1 。当存储引擎返回更新完毕后,执行器还有一个非常重要的工作要做,即备份数据 ,这个备份数据的方式就是写binlog,具体我们在3:两阶段提交 来分析。在前面我们提到执行器会调用存储引擎接口读取数据以及调用更新数据接口更新数据,这个过程我们通过2:更新语句在存储引擎做了什么? 。
2:更新语句在存储引擎做了什么?
当存储引擎收到了执行器获取数据的接口调用后,会先检测数据对应的数据页是否在内存中,如果是在,返回,否则读取磁盘获取对应的数据页,然后返回。数据在执行器经过了计算得到了需要更新的结果,又执行到存储引擎更新数据,此时存储引擎在将数据更新对应的内存后(注意此时还没有落盘) 后,会将更新信息写到redo log中,然后返回成功消息给执行器。关于redo log,具体我们在3:两阶段提交 来分析。
3:两阶段提交
在前面的分享中,我们提到了两种日志,redo log,binlog,其中redo log是InnoDB存储引擎所特有的,让MySQL具备了crash safe的能力,binlog提供了数据备份和恢复的能力。当MySQL异常重启之后恢复数据需要使用到redo log,当需要恢复数据时需要使用到binlog,因此二者在逻辑上需要保持一致性,而二者的写入又不是同步的,binlog由Server层执行器负责写入,redo log由存储引擎层的InnoDB存储引擎负责写入的,为了能够实现二者在逻辑上的一致性 ,就有了两阶段提交,以sql语句update T set c=c+1 where ID=2; 为例,这个过程如下:
1:执行器通过存储引擎接口获取ID=2的数据,数据页内存有则直接取内存中的,否则从磁盘获取,将行数据返回给执行器。
2:执行器执行数学运算,假设ID为2的c原始值为N,则经过数学运算后变为N+1,得到一行新的数据。然后调用存储引擎接口写入这行新数据。
3:存储引擎拿到这行新数据后,首先将数据更新到内存中,同时也会将更新写到redo log中,并将redo log中的记录标记为prepare状态,然后返回给执行器自己已准备完毕的消息,随时可以commit提交事务。
4:执行器生成binglog,并把binlog写入到磁盘中。
5:执行器最后调用存储引擎的事务提交接口,存储引擎会将3中redo log从prepare状态修改为commit状态。
这个过程详细的如下图:
关于redo log和binlog我们继续从接下来的内容一起看下。
3.1:redo log
InnoDB并非mysql自带的存储引擎,其自带的存储引擎是MyISAM,而MyISAM并不具备crash safe的能力。于是MySQL就以插件的形式引入了三方公司开发的存储引擎InnoDB,InnoDB存储引擎通过redo log提供了crash safe的能力,redo log设计的结构是一个环形的循环写入存储结构,该环形存储结构由若干个文件组成,比如3个文件组成的结构,如下图:
其中的红色区域代表已写入数据区域还未同步到磁盘的数据修改 ,蓝色区域代表空闲区域,write pos代表下一个可以写入redo log的位置,checkpoint代表需要同步到磁盘的开始位置。假设突然数据库宕机,因为事务已经提交了,修改就算是同步到磁盘也肯定在redo log中,假设就是红色区域的这些修改内容,再次启动数据库后只需要将红色区域的修改同步到磁盘中就可以了,这样就实现了crash safe的能力。
3.2:binlog
binlog是在server层提供的日志形式,mysql自带,日志的归档,可用于日志恢复,数据备份,增加从库时初始化数据等,binglog主要提供了两种格式的日志存储,一种是statement,另一种是row,其中statement格式存储的就是sql语句本身,row格式存储的两条语句更新前和更新后的。
|