MySQL更新过程

1. 前言

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

2. 更新流程

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

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

3. MySQL日志系统

在MySQL中,有三种日志。分别是redo log、binlog和undo log。

3.1 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.2 undo log

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

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

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

4. Write-Ahead Logging机制

  • 使用日志,存储引擎可以在内存中更新数据,然后将更新持久化到磁盘的日志文件中,不需要每次都将更新后的数据刷新到磁盘(随机IO)

  • 日志采用的是追加方式,写日志的操作实在磁盘上一小块区域的顺序IO(比随机IO快得多)

  • 日志持久化到磁盘后,内存中被修改的数据(脏页)在后台可以慢慢刷新到磁盘

5. 二阶段提交

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

当commit 命令执行时,

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

5.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记录了,从数据库会执行这条事务(或者在对数据库回到某个点时会有这条事务的执行),造成不一致的情况。

5.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讲——丁奇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章