MySql一条更新语句的执行过程

174 阅读4分钟

MySql基础架构如下:

一条更新语句要经过 连接器 分析器 优化器 执行器,比如我们执行 update T set a = a + 1 where id = 1,会将缓存中的T表数据全部清空,这也是为什么不建议使用缓存的原因。与查询流程不一样的是,更新操作还会设计二个很重要的模块 redo log(重做日志) 和 binglog(归档日志)

redo log

redo log是innodb特有的日志,更新一条数据,首先我们得从磁盘找到这条数据,然后进行更新,最后再写到磁盘中,整个过程的IO成本还有查询成本是很高的,如果数据
库的连接特别多的话,那数据库连接往往就不够用了,所以更新的时候先把更新的语句写到redo log日志文件中,等系统空闲的时候再刷新
到磁盘中,但是redo log的大小的有限的,当被写满了的时候,就需要将一些数据刷新到磁盘中去,然后将redo log空闲出来,然后继续
写。所以有了redo log即使数据库发生异常重启,数据也不会丢失,这个能力被称为crash-safe

binlog

binlog是Server层特有的日志,为什么有了redo log还会有binlog呢??再MySql没有Innodb存储引擎的时候MySql自带的MyISAM,但是
MyISAM是没有crash-safe能力的,binlog日志只能用于归档,而Innodb是另外一个公司以插件形式提供的,因为binlog没有crash-safe
能力。所以就有了redo log日志。

二种日志的区别

 1redo log是Innodb特有的,binlog是Server层的,所有引擎都可以使用
 2redo log是物理日志。记录的是在某个数据页上做了什么修改。binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一
    行的c字段加上1
 3redo log是循环写的,而binlog是追加写的,即使写满了,也会切换到下一个文件,而且不会覆盖以前的文件

现在来看看update语句的执行流程

1:执行器首先根据条件查找这一行数据,如果这行数据本来就是在内存中的就直接返回给执行器,否则就要从磁盘上查找读入内存,然后再返回
2:执行器拿到这条数据之后进行修改
3:执行器将这条新数据更新到内存中去,同时将这个更新操作添加到redo do日志文件中,此时redo log处于prepare状态,然后告知执
   行器它已经执行完成了,可以提交事务
4:执行器生成这个操作的binlog,并把binlog写入到磁盘
5:执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(Commit)状态,更新完成

我们可以发现这里使用而俩阶段提交,也就是写入redo log和binlog是要求都成功的,为什么要这样做呢??

一:先写redo log 再写binlog

  如果写入redo log成功,这时候数据宕机了,那么数据库恢复之后还是能将这条数据更新成功,比如 a = a + 1
  ,但是binlog是没有写成功的,如果那一条我们需要做数据恢复,binlog是没有这一条更新操作的,那么数据就不一致了
  

一:先写binlog,再写redo log

  如果写入binlog成功,写入redo log失败,那么数据库恢复之后这个事务无效,a的值是不会加1的,但是binlog是记录了加1的这个操
  作的,那么在以后数据恢复过程中,a的值就会被加1,与原来的值就会不同的
  

可以发现如果不使用俩阶段提交,那么数据库的状态跟它恢复过来的状态很可能会不一致

总结

  1redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成1 的时候,
       表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
  2:sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参
     数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。