热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

你不知道的Update执行过程~

你不知道的up


本文所有讨论都是基于 Innodb 存储引擎展开

主要是学习《Mysql实战45讲》+ 《Innodb存储引擎》自己笔记

上一篇介绍了 select 语句的执行过程,今天继续来看看常用的 update 语句在 mysql 中的执行过程是什么样的,整个交互过程和

首先说一下演示的表及数据信息

CREATE TABLE `user` (
  `id` int(11NOT NULL,
  `name` varchar(64DEFAULT NULL,
  PRIMARY KEY (`id`)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入三条记录
insert into user values (1"zhangsan");
insert into user values (2"lisi");
insert into user values (3"wangwu");

比如,想将 id = 1 的记录改成 'lala';整个执行过程是什么样的呢?

update user set name = 'lala' where id = 2;

update 基本过程

update 语句真正执行之前,也会做一些连接,分析器、优化器等过程,因此也是需要将 mysql 框架的基本思路走一遍。

也就是下面的流程

mysql基本架构示意图

客户端连接

客户端发出更新语句 update user set name = 'lala' where id = 2;
并向 MySQL 服务端建立连接

连接器的主要工作:建立连接、获取权限、维持和管理连接。

分析器

用于分析这条语句的作用, 比如看见 update  系统知道了这是一条更新语句

优化器

经过优化器,优化器发现 条件里用的是 id,决定要使用 id 这 个索引。

执行器

到了该阶段,系统知道了对哪个表、哪个字段进行更新了。开始执行真正的更新。注意这一步是操作引擎层的存储引擎来执行的,现在才是好戏开场了。

update 到达引擎层

我们继续沿着上图往下描述:

  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁 盘读入内存,然后再返回。

  2. 执行器拿到引擎给的行数据,把name改成lala,得到 新的一行数据,再调用引擎接口写入这行新数据。

  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。

  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

根据上面的描述继续完善上面的 mysql 基本架构图,补充存储引擎部分的工作。

update执行过程

可能图中突然冒出如此多的概念,比如redo log 、binlog,我相信对于这些概念,肯定不会陌生,下面带领大家一起一一解剖这些概念。

什么是redo log

大家想想,如果让实现数据库,该如何写数据呢?

更新一条数据,然后每次都将其写入到磁盘中,但是我们都知道磁盘的写入数据是很慢的,而且我们还不知道每次写入的数据存储到哪儿,相当于随机的去磁盘中寻找数据,找到之后,再更新数据,这个过程本身更加的慢。

为了解决写入速度慢的问题,mysql 弄了一种机制,先将需要修改的内容记录到一个固定的地方,不用寻找顺序,像是黑板似的,依次从上往下写。如下图所示:

上述描述的机制,其实就是 WAL 机制(Write-Ahead Logging),本质是先写日志,再写磁盘。日志有点像一个中转站,先将东西存放到中转站,有空再转移到仓库。

这就做到了更新数据和落磁盘解耦,不要小看简单的解耦,能够让客户端体验到快速的数据更新

左侧为WAL机制

当然,redolog 也不能无限大,InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文 件的大小是 1GB,那么这块“黑板”总共就可以记录 4GB 的操作。从头开始写,写到末 尾就又回到开头循环写,如下面这个图所示。

redo log结构  来自《mysql45讲》

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件 开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

write pos 和 checkpoint 之间的是“黑板”上还空着的部分,可以用来记录新的操作。如 果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下 来先擦掉一些记录,把 checkpoint 推进一下。[1]

正是有了 redo-log,mysql 才能够保证事务的持久性,也就是保证即使数据库发生异常重启,之前提交的记录都不会丢失。这个专业的属于叫做 crash-safe 。

为什么不能直接落盘,而非得先写日志再落磁盘呢?有何好处

1、数据库将随机写换成了顺序写,大大提升了数据库性能。

2、保证事务的持久化能力,也就是保证提交的事务不会丢失。

但是有好处必然有坏处的,由于增加了一个 "中转站",需要保证日志和磁盘两者之间一致,这就是《Innodb存储引擎内幕》中说的内存脏页问题,不过好在将日志数据刷新到磁盘的过程是异步的,并不是太影响性能。[2]

什么是binlog

为什么有了redo log还需要 bin log呢?

redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。

两者有何区别?具体区别如下

区别redo logbinlog
来源InnoDB 引擎特有的MySQL 的 Server 层实现的,所有引擎 都可以使用
记录方式物理日志,记录的是“在某个数据页上做了什么修改”逻辑日志,记录的是语句的原始逻辑,比如“将 ID=2 这一行的 name改成lala ”
存储方式循环写的,空间固定会用完追加写入的,可以不限存储,只要有不够的磁盘空间

两阶段提交

指的注意的是在上图写 redo log 和 binlog,redo log 的写入过程分成两步:prepare + commit 。

为什么需要两阶段提交呢?

以更新语句 update user set name = 'lala' where id = 2;
为例,当前 ID=2 的行,"name为lisi",再假设执 行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash, 会出现什么情况呢?

情况 1:先写 redo log 再写 binlog。

假设在 redo log 写完,binlog 还没有写完的时候, MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍 然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。

但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。

后续的备份数据,或者其他业务方监听 binlog 来同步数据时,会缺少一条更新语句导致数据不一致情况,特别是一些核心业务,可能有多个业务方监听数据库的 binlog 做同步更新,这种数据不一致完成不能接受。

备库或者其他业务方比主库少一条更新操作,这种不一致无法接受。

先写redo log 再写binlog

情况 2:先写 binlog 再写 redo log。

如果在 binlog 写完之后 crash,由于 redo log 还没写, 崩溃恢复以后这个事务无效,所以这一行 name还是lisi。但是 binlog 里面已经记录 了 “把 name 从 lisi 改成 lala” 这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事 务出来,恢复出来的这一行 name 的值就是 lala,与原库的值不同。

主库比备库或者其他业务方少一条更新操作,同样无法接受。

两阶段提交再讨论

继续回到本节的开头问题:在不同的时间段,如果发生了系统崩溃,数据如何保证一致性呢?

我们继续来看上面备份的图,着重添加了两个时刻

时刻A:写入 redo log 的 prepare 阶段,但是还没有开始写 binlog。

时刻B:写入 redo log 的 prepare 阶段,写完 binlog ,但是还没有提交事务和没有 commit redo log。

Case1:如果时刻A发生崩溃。

由于此时 binlog 还没写,redo log 也还没提交,所以崩溃恢 复的时候,这个事务会回滚。[3]这时候,binlog 还没写,所以也不会传到备库。对系统没有任何影响。

Case2:如果在时刻B发生崩溃。

这里根据崩溃恢复时的判断规则有可以分为两种情况

  1. 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;

  2. 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整:

    a. 如果是,则提交事务;

    b. 否则,回滚事务。

1. 如何判断 binlog 是否完整

一个事务的 binlog 是有完整格式的:

statement 格式的 binlog,最后会有 COMMIT;

row 格式的 binlog,最后会有一个 XID event。

同时还可以使用  binlog-checksum 参数来判断binlog是否完整。

2. 如何根据一个redo log 找到另一个binlog呢?如何正确匹配

两个都有通过的字段 叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:

如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;

如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。

3. 为什么需要两阶段提交?

主要保证事务的持久性。

如果 redo log 提交完成了,事务就不能回滚(如果这还允许回 滚,就可能覆盖掉别的事务的更新)。

而如果 redo log 直接提交,然后 binlog 写入的时 候失败,InnoDB 又回滚不了,数据和 binlog 日志又不一致了。

只有等两个日志都准备好时,再进行提交。

到这里,关于为什么需要 redo log 和 binlog做了许多阐述,两阶段提交的根本目的是保证 redo log 和 binlog 在事务上的一致性。

总结

本文主要介绍了 update 语句更新过程,其中前部分 mysql的架构比较好理解,但是 redo log 和 binlog 稍稍难以理解,关于 bin log 的作用,现在遇到比较多的场景是利用 binlog 备份数据或者 B系统用A系统的 Binlog 做数据同步操作。

参考

[1] Mysql实战45讲
[2] mysql技术内幕-innodb引擎》  
[3] http://mysql.taobao.org/monthly 数据库内核月报




推荐阅读
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
author-avatar
谢俊荣1792
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有