上一篇文章咱们说了一条查询sql的执行过程。如果没有看过上一篇文章的可以去看下上一篇文章,今天咱们说说一条更新sql的执行过程。
update scores set score=c+10 where id=1
上面一条sql是将id为1的分数加上10。
那么它的执行流程是怎样的呢?借用上篇文章的图,如下:
我这边就再简单的说一下这个流程,首先客户端连接mysql服务器,连接后执行sql语句,执行sql的过程需要经过分析器得出它是需要做update操作,再接着经过优化器它决定使用id这个索引,然后经过执行器通过索引找到这一行,最后进行更新操作。
以上就是整个更新操作得整个流程。说到这你肯定以为说完了,不过很遗憾的告诉你,这才刚刚开始呢。
因为更新操作和查询操作不一样,更新操作涉及到两个非常重要的日志模块。redo log (重做日志) 和 bin log(归档日志)。这个两个才是今天要说的重点。
首先咱们得知道这两个日志是什么?然后再得知道它们是干什么的?
redo log 是 InnoDB 引擎特有,它是属于物理日志,主要用于记录 “某个数据页上做了什么修改” ,而且它的记录空间是固定的并且是会用完的。
bin log 是属于 server 层持有的,主要是再执行器中记录日志,所以mysql所有的引擎都可以使用它。bin log 是属于逻辑日志,它有 statement 和 row 两种模式,statement记录的是执行的sql语句,row记录的是更新行的内容,所以是记录两条,一条是更新前的内容,另外一条是更新后的内容。默认模式是 row 模式。另外 bin log 是会追加写入日志,当日志文件写到一定大小的时候,就会切换到下一个继续写入日志,并且不会覆盖之前的日志文件。
以上就是这两种日志的概念以及作用,那么现在我们说说它们的记录流程。咱们先看下面一张图,黄填充色的为执行器的操作,蓝填充色为InnoDB引擎的操作
图有点长,不过应该很容易看懂。那么现在就来一步一步的分析。
1、首先执行器会找引擎取id=1这条数据;
2、因为id是主键,所以使用树来找到一行数据。不过引擎先去内存中查找是否有这一页数据;
3、如果有则直接返回数据给执行器;如果没有就会去磁盘把数据读入到内存中,然后返回数据给执行器。
4、执行器就会执行C+10操作;
5、执行器生成新的一行数据;
6、再调用 InnoDB 引擎的写入接口,把数据更新到内存中;
7、InnoDB 引擎写入 redo log 日志,标记状态为 prepare,并且告诉执行器已经更新数据完成,可以随时提交事务;
8、执行器把此操作写入 bin log ,并且把 bin log 写入磁盘;
9、最后执行器调用引擎的提交事务接口,引擎把 redo log 的状态改 commit ,至此整个更新操作完成。
看到这里也许你会冒出几个问题?
1、redo log 空间是固定,那它会不会用完呢?
首先不用担心 redo log 会用完空间,因为它是循环利用的。例如 redo log 日志配置为一组4个文件,每个文件分别为1G。它写的流程如下图:
上图中有两块填充色,黄色和红色,黄色标记着 check point,这表示这当前擦除掉 redo log 的位置,红色标记着 write pos ,这表示着当前记录 redo log 的位置。当 redo log 写满了之后,就会停下来,不再写入数据,会执行擦除 redo log 操作,当然在擦除这些日志之前,都会把数据写入到磁盘中,把数据进行持久化。这样才能保住数据的准确性。
2、为什么要用这两种日志呢?因为在没有 InnoDB 引擎的时候是没有 redo log 日志的。
因为 InnoDB 引擎的 redo log 可以保证即使数据库突然宕机了或者异常重启了,之前提交的数据是不会丢失的。这个能力咱们称之为 crash-safe。
3、当mysql服务器在执行过程中突然间宕机了,数据会不会丢失?
答案是不会。为什么这么肯定呢?我们可以从第二张图就可以看出来。比如有以下几种情况:
1、写入 redo log 之前宕机了,那么原始数据是不会发送改变的,因为还没有进行事务的提交。
2、如果写入 redo log 之后,写入 bin log之前宕机,那么原始数据还是不会变,因为数据库重启后,因为两种日志的记录没有同步,所以不会有新数据生成。
3、在 redo log 生成commit之前宕机了,数据库重启后 数据会变成更新后的数据,因为这个时候 redo log 和 bin log 都有了记录,所以数据库重启后会自己进行commit,所以这时候的数据就是更新后的数据了。
我们使用 redo log 主要是需要保证 crash-safe 能力,innodb_flush_log_at_trx_commit这个参数设置成1的时候,表示每次事务的redo log都直接持久化到磁盘。这个参数我建议你设置成1,这样可以保证MySQL异常重启之后数据不丢失。
sync_binlog这个参数设置成1的时候,表示每次事务的binlog都持久化到磁盘。这个参数我也建议你设置成1,这样可以保证MySQL异常重启之后binlog不丢失。
好了,今天就说到这里,如果写的有误的地方欢迎大家指出,咱们一起讨论学习。