Innodb存储引擎-执行流程分析和二阶段提交分析

执行流程分析

基本流程

(1)连接,分析,优化,执行

客户端与MySQL Server建立连接,发送语句给MySQL Server,接收到后如果是查询语句会先去查询缓存中看,有的话就直接返回了,(新版本的MySQL已经废除了查询缓存,命中率太低了),如果是缓存没有或者是非查询语句,会进行语法分析并创建一个解析树,然后进行优化,(解析器知道语句是要执行什么,会评估使用各种索引的代价,然后去使用索引,以及调节表的连接顺序)然后调用innodb引擎的接口来执行语句。

(2)写undo log

innodb 引擎首先开启事务,获得一个事务ID(是一直递增的),根据执行的语句生成一个反向的语句,(如果是INSERT会生成一条DELETE语句,如果UPDATE语句就会生成一个UPDATE成旧数据的语句),用于提交失败后回滚,将这条反向语句写入undo log,得到回滚指针,并且更新这个数据行的回滚指针和事务id。(事务提交后,Undo log并不能立马被删除,而是放入待清理的链表,由purge 线程判断是否有其他事务在使用undo 段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间,简单的说就是看之前的事务是否提交成功,这个事务及之前的事务都提交成功了,这部分undo log才能删除)

(3)从索引中查找数据

根据索引去B+树中找到这一行数据(如果是普通索引,查到不符合条件的索引,会把所有数据查找出来,唯一性索引查到第一个数据就可以了)

(4)更新数据

判断数据页是否在内存中?

  1. 数据页在内存中。若为普通索引,直接更新内存中的数据页;若为唯一性索引,判断更新后是否会数据冲突(不能破坏索引的唯一性),不会的话就更新内存中的数据页。
  2. 数据页不在内存中。若为普通索引,将对数据页的更新操作记录到change buffer,在下一次查询时需要访问这个数据页时,再执行change buffer中的操作对数据页进行更新。(或者是在MySQL Server空闲时,会将change buffer中所有操作更新到磁盘,也就是俗称的‘刷页’。);若为唯一性索引,因为需要保证更新后的唯一性,所以不能延迟更新,必须把数据页从磁盘加载到内存,然后判断更新后是否会数据冲突,不会的话就更新数据页。

(5)写redo log(prepare状态)

将对数据页的更改写入到redo log,此时redo log中这条事务的状态为prepare状态。

(6)写bin log(同时将redo log设置为commit状态)

更新时,先改内存中的数据页,将更新操作写入redo log日志,此时redo log进入prepare状态,然后通知MySQL Server执行完了,随时可以提交,MySQL Server将更新的SQL写入bin log,然后调用innodb引擎接口将redo log设置为提交状态,更新完成。

WAL(Write-Ahead Log)

Write-ahead Log的思路:先在内存中提交事务,然后写日志(所谓的Write-ahead Log),然后后台任务把内存中的数据异步刷到磁盘。日志是顺序地在尾部Append,从而也就避免了一个事务发生多次磁盘随机 I/O 的问题。明明是先在内存中提交事务,后写的日志,为什么叫作Write-Ahead呢?这里的Ahead,其实是指相对于真正的数据刷到磁盘,因为是先写的日志,后把内存数据刷到磁盘,所以叫Write- Ahead Log。

具体到InnoDB中,Write-Ahead Log是Redo Log。在InnoDB中,不 光事务修改的数据库表数据是异步刷盘的,连Redo Log的写入本身也是异步的。

write和fsync区别

这里我们首先要明确两个概念和两个参数:

  • write:刷盘。指的是MySQL从buffer pool中将内容写到系统的page cache中,并没有持久化到系统磁盘上。这个速度其实是很快的。
  • fsync:同步持久化到磁盘。指的是从系统的cache中将数据持久化到系统磁盘上。这个速度可以认为比较慢,而且也是IOPS升高的真正原因。
innodb_flush_logs_at_trx_commit & sync_binlog

innodb_flush_logs_at_trx_commit(redo log):

  • 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作,每次提交事务都只把redo log留在redo log buffer中
  • 1(默认值) :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值),每次提交事务都将redo log 持久化到磁盘上,也就是write+fsync
  • 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache ,也就是只write,不fsync

sync_binlog(binlog)

  • 0(默认值):当事务提交之后,将binlog 从binlog cache中 write到磁盘上,不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让File system自行决定什么时候来做同步,或者cache满了之后才同步到磁盘,性能最佳
  • 1:当每进行1次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘,是最慢的,但是最安全
  • N:每次提交事务都将binlog write到磁盘上,当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

参考:MySQL日志(redo log、binlog)刷盘策略 - Philosophy - 博客园 (cnblogs.com)

二阶段提交

正常情况下两阶段提交配合组提交的流程如下:

在双一模式下的流程,每个事务提交时都需要进行fsync刷盘,其执行过程才如上图所示。然而在配合两个参数sync_binlog和innodb_flush_log_at_trx_commit,上图会有所调整。

innodb_flush_log_at_trx_commit在非1的情况下,步骤三可以忽略:

  • innodb_flush_log_at_trx_commit=0,redo log日志条目写入到redo log buffer中,MySQL即认为redo log已完成写入,即redo log prepare状态,可以进行下一步动作。
  • innodb_flush_log_at_trx_commit=2,redo log日志条目写入到文件系统缓存page cache,MySQL即认为已完成redo log完成写入,即redo log prepare状态,可以进行下一步动作。此时的刷盘由MySQL的后台主线程和操作系统层进行完成。

sync_binlog在非1的情况下,步骤四可以进行忽略:

  • sync_binlog=0,binlog只需要写入到binlog cache即可进行下一步,此刻MySQL会认为binlog已完成写入,redo log和binlog达成一致,redo log可以commit。
  • sync_binlog=N(N>1),每个事务的binlog都写入到binlog cache,攒够N个事务之后,集中fsync刷盘(由后台主线程处理),此刻MySQL会认为binlog已完成写入,redo log和binlog达成一致,redo log可以commit。

那么两段提交过程中失败会发生什么结果呢?MySQL又是怎样处理的呢?

首先我们先放一下两段提交的图:

接下来,我们就一起分析一下在两阶段提交的不同时刻,MySQL 异常重启会出现什么现象。

如果在图中时刻 A 的地方,也就是写入 redo log 处于 prepare 阶段之后、写 binlog 之前,发生了崩溃(crash),由于此时 binlog 还没写,redo log 也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候,binlog 还没写,所以也不会传到备库。

如果在图中在时刻 B,也就是 binlog 写完,redo log 还没 commit 前发生 crash,那崩溃恢复的时候 MySQL 会怎么处理?

我们先来看一下崩溃恢复时的判断规则:

  • 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;
  • 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:
    • 如果是,则提交事务;
    • 否则,回滚事务。

这里,时刻 B 发生 crash ,崩溃恢复过程中事务会被提交。

那么问题又来了,MySQL是如何判断binlog是不是完整的呢?

我们都知道binlog有三种格式statement、row、mix。其中mix是前两种方式的组合,一个事务的binlog是有完整的格式的,

  • statement 格式的 binlog,最后会有 COMMIT;
  • row 格式的 binlog,最后会有一个 XID event。

另外,在 MySQL 5.6 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。所以,MySQL 还是有办法验证事务 binlog 的完整性的。

而且redolog和binlog有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交; 如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。这样在两段提交的前提下就能完全保证事务的特性了。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值