mysql更新的底层处理_mysql系列之更新语句的底层逻辑

概述

本文将从一条简单的单表更新sql出发,探析mysql执行更新sql的底层实现逻辑。

mysql基本架构

ea95f90cf6cdac0e0232bb887e38232d.png

上图就是Mysql的逻辑架构图。大体来说,MySQL可以分为Server层和存储引擎层两部分。

Server层 包括连接器、查询缓存、分析器、优化器、执行器等,涵盖mysql绝大多数的核心功能。

存储引擎层 负责数据的存储和提取。mysql支持InnoDB、MyISAM、Memory等多个存储引擎。从mySQL 5.5.5版本开始,InnoDB成为了mysql的默认存储引擎。

mysql更新语句执行流程

假设有如下简单的表T:

mysql> create table T(ID int primary key, c int);

复制代码

现在要将ID=2这一行的值加1:

mysql> update T set c=c+1 where ID=2;

复制代码

我们现在就通过这条简单的更新语句来探析mysql更新语句的底层实现逻辑。之前我们在mysql系列之查询语句的底层逻辑 文章中已经介绍了查询语句的基本执行链路。之前查询语句经过的流程,更新语句也会经过。所不同的是:

执行更新操作时,会使这个表的所有查询缓存失效,这也就是不建议使用查询缓存的原因。

更新流程还涉及到两个重要的日志模块:redo log(重做日志) 和 binlong(归档日志)日志。

下面我们就来介绍这两个日志模块。

日志模块:redo log

作用

当有一条记录需要更新时,InnoDB引擎会将记录写到redo log日志中,写的是物理日志,并更新内存,这时候更新操作就完成了。当系统比较空闲,或者redo log日志满了的时候,InnoDB就会将部分操作记录更新到磁盘中,并且释放redo log日志对应的空间。

redo log的存在,保证了即使数据库发生异常重启,之前提交的记录也不会丢失。因为记录提交时,一般不会立即刷新到磁盘,而redo log中已经保存了相应的操作日志了。所以数据库发生异常重启之后,就可以使用redo log来恢复数据。

结构

redo log的大小一般时固定的,可以被配置为一组文件,按顺序写入。

5a01fa4372ad984a06955758e0cc94f6.png

如上图,write pos是当前记录的位置,一边写一边往后移,移动到最后一个文件末尾的时候,就回到了第一个文件的开头,不断循环。checkpoint是当前要擦除的位置,也是不断地后移循环的。write pos和checkpoint之间空出的位置,可以用来记录新的操作。

日志模块:binlog

作用

binlog是mysql的server层实现的,与redolog的功能相似,也会记录数据更新操作日志。不过日志是以逻辑日志的形式存在的。

结构

binlog是可以追加写入的,文件写到一定大小后会自动切换到下一个,不会覆盖之前的日志。

redo log与binlog的区别

redolog

bin log

使用范围

是InnoDB引擎特有的

是Server层实现的,所有引擎都可以使用

日志类型

物理日志,记录的是在哪个数据页上的变更操作

逻辑日志,记录的是原始逻辑

写入方式

循环写,大小固定

追加写入,不会覆盖旧日志

redo log与binlog相结合,实现数据的更新

160f72464e70375ed71c77a0b3712064.png

上图就是update语句的执行流程,其中浅色框代表在InnDB内部执行,深色框代表在执行器中执行。这里就涉及到了redolog和binlog的协同配合,以及两阶段提交的处理流程。

两阶段提交

看到这里你也许会有疑问?为什么要使用两阶段提交?为什么要redo log与binlog相结合?下面我们来具体分析一下。

首先我们要先明确一下redo log和binlog两者的职责。redo log是InnoDB独有的,指定引擎为InnoDB时,就开启了redo log,它存储着物理日志。数据更新时,都是先更新到内存,在写入到redo log的。只有当系统比较空闲,或者redo log空间满时,才会将redo log的数据写入磁盘中。

而bin log是service层产生的,为所有的存储引擎所共享,它记录的是逻辑日志,只要有进行数据更新操作,binlog中都有对应的记录,而且是追加写入的,不会覆盖之前的日志。

现在说明为什么要实现两阶段提交。由于redo log和binlog是两个独立的逻辑,如果不用两阶段提交,要么就是先写完redo log再写binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。

假设当前ID=2的行,字段c的值是0,执行pdate T set c=c+1 where ID=2;这条语句。

先写redo log,后写binlog。 假设redo log写完之后,还没来得及写完binlog的时候,数据库崩溃了。这时候binlog中还没有记录update T set c=c+1 where ID=2;这条语句。因此,如果用binlog来备份临时库的时候,就会丢失这个修改,也就是恢复出来的这一行字段c的值就是0。而原库中redo log日志写入磁盘后,字段c的值变成了1,所以就出现了原库和备库数据不一致的问题。

先写binlog,后写redo log。 假设binlog写完之后,还没来得及写完redo log的时候,数据库就崩溃了。这时候binlog中已经记录了update T set c=c+1 where ID=2;这条语句。但由于redo log还没写,所以事务无效。也就是说,用binlog来进行备库时,这一行的c的值时1,但是原库中这一行c的值还是0,所以还是出现了原库和备库数据不一致的问题。

综上,两阶段提交的思路就是先写入redo log,但是不立即提交事务,这时候redo log处于prepare阶段,接着再写bin log,最后,执行器调用引擎的提交事务接口,将redo log修改成commit状态,更新完成。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值