MySQL更新过程

1. 前言

一条简简单单的更新操作涉及到太多的知识点了,首先我们要1.了解MySQL各个功能模块,然后在MySQL更新时不仅仅是操作表数据还要操作2.日志系统redo log、binlog和undo log,更新操作也不是实时更新到磁盘的而是通过3.Write-Ahead Logging机制先刷到内存再刷到磁盘,要刷到内存写日志的顺序又涉及到4.二阶段提交。如果数据涉及到索引还要对5.change buffer进行操作

2. MySQL日志系统

在MySQL中,有三种日志。分别是redo log、binlog和undo log。在更新过程中要对日志进行更新。

2.1 redo log(重做日志)

Write-Ahead Logging机制

在数据更新过程中,如果每次的更新操作都需要写进磁盘,磁盘也要找到对应的那条记录然后再更新,整个过程IO成本、查找成本都很高。MySQL使用WAL技术(全称Write-Ahead Logging)解决每次都要写磁盘的问题。WAL的关键点是先写日志再写磁盘。具体来说,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面并更新内存,InnoDB引擎会在适当的时候将这个操作记录更新到磁盘里面(这个操作往往是在系统比较空闲的时候做的)。

redo log的结构

InnoDB的redo log是固定大小的,比如可以配置一组4个文件,每个文件大小是1G。那么redo log总共可以记录4GB的操作,从头开始写,写到末尾又回到开头循环写。当写满时擦除一部分记录。存储的是物理逻辑(xxxx页修改了xxx)。

redo log的结构

InnoDB的crash-safe能力

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。保证ACID特性的持久性。当异常重启时,数据库根据磁盘数据和redo log对比保证数据正常。

2.2 binlog(归档日志)

为什么有两份日志?

binlog是Server层的日志,redo log是InnoDB引擎特有的日志。最开始MySQL里并没有InnoDB引擎,MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力, binlog日志只能用于归档。 而InnoDB是另一个公司以插件形式引入MySQL的, 既然只依靠binlog是没有crash-safe能力的, 所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。

redo log 和 binlog的区别

1、存储的内容

可以这样理解,binlog记载的是update/delete/insert这样的SQL语句,而redo log记载的是物理修改的内容(xxxx页修改了xxx)。所以在搜索资料的时候也会有这样的说法:binlog 记录的是数据的逻辑变化redo log 记录的是数据的物理变化

2、功能

redo log的作用是实现持久化。数据库更新写完内存中的数据,如果数据库挂了,那我们可以通过redo log来恢复内存还没来得及刷到磁盘的数据,将redo log加载到内存里边,那内存就能恢复到挂掉之前的数据了。

binlog的作用是进行复制和恢复

  • 主从服务器需要保持数据的一致性,通过binlog来同步数据。
  • 如果整个数据库的数据都被删除了,binlog存储着所有的数据变更情况,那么可以通过binlog来对数据进行恢复。

3、载体

redo log是InnoDB引擎特有的。binlog是MySQL的Server层实现的,所有引擎都可以使用。

4、记录方式

redo log是循环写的,空间固定会用完;binlog是追加写入的,一个文件写满后会切换到下一个文件而不会去覆盖以前的日志。

3.3 undo log

undo log主要有两个作用:回滚和多版本并发控制(MVCC)

在数据修改的时候,不仅记录了redo log,还记录undo log,如果因为某些原因导致事务失败或回滚了,可以用undo log进行回滚undo log主要存储的也是逻辑日志,比如我们要insert一条数据了,那undo log会记录的一条对应的delete日志。我们要update一条记录时,它会记录一条对应相反的update记录。

回滚的实现就是找到undo log中对应的相反操作语句执行。
而多版本并发控制则是利用undo log做版本的回退(聊MVCCC时再具体讲)

3. 更新流程

首先是MySQL的各个功能模块,在MySQL查询过程会详细介绍。

以更新ID为2的行的某个值+1为例:

  1. 数据库连接,然后通过分析器进行词法分析和语法分析,再经过优化器选择索引等。
  2. **取数据:**执行器先找引擎取ID=2这一行。 ID是主键, 引擎直接用树搜索找到这一行。 如果ID=2这一行所在的数据页本来就在内存中, 就直接返回给执行器; 否则, 需要先从磁盘读入内存, 然后再返回。
  3. **更新数据:**执行器拿到引擎给的行数据, 把这个值加上1, 比如原来是N, 现在就是N+1, 得到新的一行数据, 再调用引擎接口写入这行新数据到内存(使用change buffer则不会马上更新到磁盘)。
  4. 将这个更新操作记录到redo log里面, 此时redo log处于prepare状态。 然后告知执行器执行完成了, 随时可以提交事务。
  5. 执行器生成这个操作的binlog, 并把binlog写入磁盘。
  6. 执行器调用引擎的提交事务接口, 引擎把刚刚写入的redo log改成提交(commit) 状态, 更新完成。

4. 二阶段提交

上图执行过程中,redo log分为prepare阶段和commit阶段,在写入binlog的前后执行,这就是二阶段提交。

当commit 命令执行时,

  • 写入redo log进入prepare阶段:事务进入commit prepare 阶段,事务中新生成的redo log 会被刷到磁盘
  • 写入bin log:把binlog日志刷到磁盘
  • redo log处于commit状态更新完成:innodb释放锁,清除undo信息,设置redo log提交状态。

4.1 为什么需要二阶段提交

由于存在redo log 和 binlog ,而他们两是相互独立的。而事务提交必须确保两者同时有效。不然会出现不一致的情形。我们对redo log和binlog不进行二阶段提交的顺序进行假设。

**先写redo log再写binlog:**redo log写了,binlog还没写,数据库崩了。通过redo log恢复数据库能将这条事务执行,但是binlog没有记录,从数据库就不能执行这条事务(或者在对数据库回到某个点时会没有这条事务),造成不一致的情况。

**先写binlog再写redo log:**binlog写了,redo log还没写,数据库崩了。通过redo log恢复数据库没有这条事务,但是binlog记录了,从数据库会执行这条事务(或者在对数据库回到某个点时会有这条事务的执行),造成不一致的情况。

4.2 二阶段提交怎么解决问题

上图的①时出现问题怎么解决?

这个时候redo log已经到磁盘了。binlog没有刷到磁盘所以会消失。服务器从故障中恢复时,读取磁盘中的redo log ,但是由于对应的redo log项还是prepare状态,就要判断binlog 是否完整,如果binlog完整则提交事务,如果binlog不完整则回滚事务

上图的②时出现问题怎么解决?

这个时候redo log 和 binlog都已经存磁盘,服务器从redo log恢复就好了。

怎么判断binlog的完整性?

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

注:binlog有三种模式:statement、row和mixed(statement和row的混合)

找到redo log后怎么找到binlog?

redo log和binlog有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。

参考文章:

浅谈mysql的两阶段提交协议
MySQL实战45讲——丁奇

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值