MySQL 查询执行的过程链接
Select语句的执行过程会经过连接器、分析器、优化器、执行器、存储引擎,同样的 Update语句也会同样走一遍 Select语句的执行过程。
但是和 Select最大不同的是,Update语句会涉及到两个日志的操作 redo log
(重做日志) 和 binlog
(归档日志)。
那么 Mysql中又是怎么使用 redo log
和 binlog
?为什么要使用 redo log
和 binlog
呢?直接执行更新然后存库不就行了吗?还要放在 redo log
和 binlog
中,这不是多此一举吗?且听我慢慢道来,这里面大有文章。
redo log
大家都是知道 Mysql是关系型数据库,用来存储数据的,在访问数据库量大的时候,Mysql读写磁盘访问的效率是非常低的,加上 sql中的条件对数据的筛选过滤,那么效率就更低了。这也是为什么引入非关系型数据库作为作为数据缓存原因,例如:Redis
、MongoDB
等,就是为了减少 sql执行期间的数据库 io操作。
同样的道理,若是每次执行 update语句都要进行磁盘 io操作、以及数据的过滤筛选,小量的访问和数据量数据库还可以撑住,那么访问量一大以及数据量一大,这样数据库肯定顶不住。基于上面的问题于是出现了redo log
日志,redo log日志也叫做 WAL技术(Write-Ahead Logging
),他是一种先写日志,并更新内存,最后再更新磁盘的技术,并且更新磁盘往往是在 Mysql比较闲的时候,这样就大大减轻了 Mysql的压力。
redo log的特点就是:redo log是固定大小,是物理日志,属于 InnoDB引擎的,并且写 redo log是环状写日志的形式:
如上图所示:若是四组的redo log
文件,一组为1G的大小,那么四组就是4G的大小,其中 write pos
是记录当前的位置,有数据写入当前位置,那么write pos就会边写入边往后移。而 check point
是擦除的位置,因为redo log是固定大小,所以当redo log满的时候,也就是 write pos追上 check point的时候,需要清除 redo log的部分数据,清除的数据会被持久化到磁盘中,然后将 check point向前移动。redo log日志实现了即使在数据库出现异常宕机的时候,重启后之前的记录也不会丢失,这就是 crash-safe能力。
binlog
binlog称为归档日志,是逻辑上的日志,它属于 Mysql Server层面的日志,记录着 sql的原始逻辑,主要有两种模式,一个是 statement格式记录的是原始的sql,而 row格式则是记录行内容。那么这样看来 redo log和 binlog虽然记录的形式、内容不同,但是这两者日志都能通过自己记录的内容恢复数据,那么为什么还要这两个日志同时存在呢?只要其中一个不就行了嘛,两个同时存在不就多此一举了嘛。且听我慢慢道来,这里面也大有文章。
因为刚开 Mysql自带的引擎 MyISAM就没有 crash-safe功能的,并且在此之前 Mysql还没有 InnoDB引擎,Mysql自带的 binlog日志只是用来归档日志的,所以 InnoDB引擎也就通过自己 redo log日志来实现 crash-safe功能。
update执行过程
上面说了那么久两种日志的作用和特点,那么这两种日志究竟和 update执行语句有什么关系呢?先来看图:
前提:当前的引擎是使用InnoDB,update语句与 select语句区别主要是这两日志的使用主要是在执行器和引擎之间进行交互时体现的区别。假如执行如下一条简单的更新语句是:
update user set age=age+1 where id =2;
上面说过 select语句走过的流程 update语句也会走一遍,当来到执行器的时候:
【1】执行器会调用引擎的读接口,然后找到 id=2的数据行,因为 id是主键索引,索引按照树的搜索找到这一行, 若是数据行已经存在于内存的数据页中就会立即将结果返回,若是不在内存中,就会从磁盘中进行加载到内存中,然后将查询的结果返回。
【2】执行器将返回的结果的age字段+1,并调用引擎的写接口写入更新后的数据行。
【3】引擎获取到更新后的数据行更新到内存和 redo log
(也是写入磁盘,顺序写入,比较快)中,并告诉执行器可以随时提交事务,此时的 redo log
处于 prepare
阶段。
【4】执行器收到引擎的告知后,生成 binlog
日志,并且调用引擎的接口提交事务,引擎将 redo log
的状态修改为 commit
状态,这样这个更新操作算是完成。
两阶段提交
上面详细的说了update语句的执行流程,提到了redo log的 prepare 和 commit两个阶段,这就是两阶段提交,两阶段提交的目的是为了保证 redo log日志与 binlog日志保持数据的一致性。若是 redo log写成功 binlog写失败,或者 redo log写失败 binlog写成功,最后使用这两者日志进行数据恢复得到的结果数据都是不一致性的,所以为了保证两个日志逻辑上的一致,使用两阶段进行提交。
redo log 与 binlog的总结
最后来对比一下这两种日志:redo是物理的,binlog是逻辑的,redo的大小固定,并且以环状的形式写入数据,数据满的时候需要将 redo日志中擦除数据,并且将擦除的数据持久化到磁盘中。而 binlog以追加日志的形式写入,也就是当日志写到一定大小后,就会切换到下一个,并不会覆盖以前写的日志。binlog 是在 Mysql Server层中使用,因为 binlog没有 crash-safe功能,所以 InnoDB引擎自己实现了 redo log日志的 crash-safe的功能,为了保证这两个日志逻辑上的一致使用两阶段提交。在使用 redo和 binlog这两种日志的时候,可以将参数 innodb_flush_log_at_trx_commit
和 sync_binlog
都设置为1,它表示每次事务提交的时候,都会将日志持久化到磁盘中。