一条SQL更新语句是如何执行的?( 四 )

  • binlog日志是用来归档的,binlog以事件的形式记录了所有的 DDL和 DML 语句(因为它记录的是操作而不是 数据值,属于逻辑日志),但是不具备宕机恢复的功能,因为可能没有来得及刷新脏页,造成脏页数据的丢失,而这些操作也没有保存到binlog中从而造成数据丢失;
  • binlog记录的是关于一个事务的具体操作内容,即该日志是逻辑日志 。而redo日志记录的是关于每个页的更改的物理情况 。功能压根不是一回事儿 。
  • 6.2 binlog日志的作用6.2.1 主从复制binlog是实现MySQL主从复制功能的核心组件 。
    master节点会将所有的写操作记录到binlog中,slave节点会有专门的I/O线程读取master节点的binlog,将写操作同步到当前所在的slave节点 。
    一条SQL更新语句是如何执行的?

    文章插图
    6.2.2 数据恢复假如你在阅读这篇文章的时候觉得我写得实在太好,拍案叫绝的时候一不小心把公司的数据库给删了,你该怎么做才能恢复到你删库之前的那个时刻的状态?
    这个时候就要用到binlog了,前提是binlog没有被删除,否则,神仙也救不了你了 。
    通常情况下,公司会定期对数据库进行全量备份,可能隔一个月,一周,甚至可能每天都备份一次 。运气好的话你可以使用前一天的全量备份,恢复到前一天的某时刻状态(或者一周、一月之前),然后从全量备份的时刻开始,从binlog中提取该时刻之后(前提是你的binlog里面存放了这段时间的日志)的所有写操作(当然,你得过滤掉你的删库操作),然后进行操作回放就可以了 。
    是不是很简单?
    问题又来了 。再看一眼我们的更新语句 。
    update t_user_innodb set name ='chanmufeng1994' where id = 1;
    假如这条更新语句已经被写入到了redo日志,还没来得及写binlog的时候,MySQL宕机重启了,我们看一下会发生什么 。
    因为redo日志可以在重启的时候用于恢复数据,所以写入磁盘的是chanmufeng1994 。但是binlog里面没有记录这个逻辑日志,所以这时候用binlog去恢复数据或者同步到从库,就会出现数据不一致的情况 。
    所以在写两个日志的情况下,就类似于「分布式事务」的情况,如果你不清楚分布式事务是个什么东西也没关系,我在之后的文章会介绍到 。能够明确的就是redo日志和binlog日志如果单纯依次进行提交是无法保证两种日志都写成功或者都写失败的 。
    我们需要「两阶段提交」 。
    6.3 两阶段提交
    两阶段提交不是MySQL的专利,两阶段提交是一种跨系统维持数据逻辑一致性的常见方案,尤其在分布式事务上,所以请读者重点体会思想
    我们把redo日志的提交分成两步,两步中redo日志的状态分别是prepare和commit 。步骤如下
    1. InnoDB存储引擎将更改更新到内存中后,同时将这个更新操作记录到redo日志里面,此时redo日志处于prepare状态;
    2. 执行器生成这个操作的binlog,并将binlog刷盘;
    3. 执行器调用InnoDB的提交事务接口,InnoDB把刚刚写入的redo日志改成commit状态 。至此,所有操作完成 。

    一条SQL更新语句是如何执行的?

    文章插图
    加上两阶段提交之后我们再来看一下SQL更新语句的执行流程 。
    7. SQL更新语句的执行总结——终版
    一条SQL更新语句是如何执行的?

    文章插图
    1. 客户端发送更新命令到MySQL服务器,经过处理连接、解析优化等步骤;
    2. Server层向InnoDB存储引擎要id=1的这条记录;
    3. 存储引擎先从缓存中查找这条记录,有的话直接返回,没有则从磁盘加载到缓存中然后返回;
    4. Server层执行器修改这条记录的name字段值;
    5. 存储引擎更新修改到内存中;
    6. 存储引擎记录redo日志,并将状态设置为prepare状态;
    7. 存储引擎通知执行器,修改完毕,可以进行事务提交;
    8. Server先写了个binlog;
    9. Server提交事务;
    10. 存储引擎将redo日志中和当前事务相关的记录状态设置为commit状态 。
    完!

    【一条SQL更新语句是如何执行的?】


    推荐阅读