0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

select语句和update语句分别是怎么执行的

数据分析与开发 来源:数据分析与开发 作者:数据分析与开发 2020-11-03 09:41 次阅读

最近有粉丝面试互联网公司被问到:你知道select语句和update语句分别是怎么执行的吗?,要我写一篇这两者执行SQL语句的区别,这不就来了。

总的来说,select和update执行的逻辑大体一样,但是具体的实现还是有区别的。

当然深入了解select和update的具体区别并不是只为了面试,当希望Mysql能够高效的执行的时候,最好的办法就是清楚的了解Mysql是如何执行查询的,只有更加全面的了解SQL执行的每一个过程,才能更好的进行SQl的优化。

select语句

当执行一条查询的SQl的时候大概发生了以下的步骤:

客户端发送查询语句给服务器。

服务器首先进行用户名和密码的验证以及权限的校验。

然后会检查缓存中是否存在该查询,若存在,返回缓存中存在的结果。若是不存在就进行下一步。

接着进行语法和词法的分析,对SQl的解析、语法检测和预处理,再由优化器生成对应的执行计划。

Mysql的执行器根据优化器生成的执行计划执行,调用存储引擎的接口进行查询。

服务器将查询的结果返回客户端。

执行的流程

Mysql中语句的执行都是都是分层执行,每一层执行的任务都不同,直到最后拿到结果返回,主要分为Service层和引擎层,在Service层中包含:连接器、分析器、优化器、执行器。引擎层以插件的形式可以兼容各种不同的存储引擎。

Mysql的执行的流程图如下图所示:

这里以一个实例进行说明Mysql的的执行过程,新建一个User表,如下:

//新建一个表 DROPTABLEIFEXISTSUser; CREATETABLE`User`( `id`int(11)NOTNULLAUTO_INCREMENT, `name`varchar(10)DEFAULTNULL, `age`intDEFAULT0, `address`varchar(255)DEFAULTNULL, `phone`varchar(255)DEFAULTNULL, `dept`int, PRIMARYKEY(`id`) )ENGINE=InnoDBAUTO_INCREMENT=40DEFAULTCHARSET=utf8; //并初始化数据,如下 INSERTINTOUser(name,age,address,phone,dept)VALUES('张三',24,'北京','13265543552',2); INSERTINTOUser(name,age,address,phone,dept)VALUES('张三三',20,'北京','13265543557',2); INSERTINTOUser(name,age,address,phone,dept)VALUES('李四',23,'上海','13265543553',2); INSERTINTOUser(name,age,address,phone,dept)VALUES('李四四',21,'上海','13265543556',2); INSERTINTOUser(name,age,address,phone,dept)VALUES('王五',27,'广州','13265543558',3); INSERTINTOUser(name,age,address,phone,dept)VALUES('王五五',26,'广州','13265543559',3); INSERTINTOUser(name,age,address,phone,dept)VALUES('赵六',25,'深圳','13265543550',3); INSERTINTOUser(name,age,address,phone,dept)VALUES('赵六六',28,'广州','13265543561',3); INSERTINTOUser(name,age,address,phone,dept)VALUES('七七',29,'广州','13265543562',4); INSERTINTOUser(name,age,address,phone,dept)VALUES('八八',23,'广州','13265543563',4); INSERTINTOUser(name,age,address,phone,dept)VALUES('九九',24,'广州','13265543564',4);

现在针对这个表发出一条SQl查询:查询每个部门中25岁以下的员工个数大于3的员工个数和部门编号,并按照人工个数降序排序和部门编号升序排序的前两个部门。

SELECTdept,COUNT(phone)ASnumFROMUserWHEREage< 25 GROUP BY dept HAVING num >=3ORDERBYnumDESC,deptASCLIMIT0,2;

连接器

开始执行这条sql时,首先会校验你的用户名和密码是否正确,若是不正确会返回错误信息:"Access denied for user";

若是用户名和密码校验通过,然后就会到权限表获取当前用户拥有的权限,会检查该语句是否有权限,若是没有权限就直接返回错误信息,有权限会进行下一步,校验权限的这一步是在图一的连接器进行的,对连接用户权限的校验。

注意:后续的一些列操作都是依赖于这个权限的范围内的。

检索缓存

当建立连接,履行查询语句的时候,会先行检查在缓存区域看看这个sql与否履行过,若是之前执行过,它的执行结果会以Key-Value的形式缓存于内存中,Key是执行的sql,Value是结果集。

假如,缓存中key遭击中,便会直接将结果返回给客户端,假如没命中,便会履行后续的操作,完工之后亦会将结果缓存起来以便再次查询获取,当下一次进行查询的时候也是如此的循环操作。

注意:Mysql中的缓存比较适合于那些静态的表,更新不频繁的表,因为只要当前表有数据更新,有关于该表的缓存就会失效,若是表更新频繁缓存频繁的失效,这样维护缓存的消耗的性能远大于使用缓存带来的性能优化,这样就会得不偿失,严重影响Mysql的性能,所以在Mysql 8版本中的时候把缓存这一块给砍掉了。

在个人的观点中对于缓存这一块的看法是,没必要砍掉,可以设置成默认关闭缓存,需要的时候再设置开启,并且可以通过配置参数指定特定的表使用缓存,那些表不使用缓存,这样或许使用缓存更有效。

分析器

分析器主要有两步:(1)词法分析(2)语法分析

词法分析主要执行提炼关键性字,比如select,提交检索的表,提交字段名,提交检索条件,确定该语句是select还是update或者是delete语句。

语法分析主要执行辨别你输出的sql与否准确,是否合乎mysql的语法,若是不符合sql语法就会抛出:You have an error in your SQL syntax。

优化器

查询优化器会将解析树转化成执行计划。一条查询可以有多种执行方法,最后都是返回相同结果。优化器的作用就是找到这其中最好的执行计划。

例如:在查询语句中有多个索引的时候,优化器决定使用哪一个索引,或者有多表关联的时候,决定表的连接顺序等这些操作都是在优化器决定的。

生成执行计划的过程会消耗较多的时间,特别是存在许多可选的执行计划时。如果在一条SQL语句执行的过程中将该语句对应的最终执行计划进行缓存。

当相似的语句再次被输入服务器时,就可以直接使用已缓存的执行计划,从而跳过SQL语句生成执行计划的整个过程,进而可以提高语句的执行速度。

MySQL使用基于成本的查询优化器。它会尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最少的一个。

执行器

优化器生成得执行计划,交由执行器进行执行,执行器调用存储引擎得读接口,执行器中循环的调用存储引擎的读接口,以此换取满足条件的数据行,并把它放在一个结果集中,遍历并获取了所有满足条件的数据行,最后将结果集返回,结束整个查询得过程。

update语句

上面我们说完了select语句,select语句的执行过程会经过连接器、分析器、优化器、执行器、存储引擎,同样的update语句也会同样走一遍select语句的执行过程。

但是和select最大不同的是,update语句会涉及到两个日志的操作redo log(重做日志)和binlog(归档日志)。对于这两个日志的详细介绍,我之前写过一篇文章进行介绍,有兴趣的可以看一看[]:

那么Mysql中又是怎么使用redo log和binlog?为什么要使用redo log和binlog呢?直接执行更新然后存库不就行了吗?还要放在redo log和binlog中,这不是多此一举吗?且听我慢慢道来,这里面大有文章。

redo log

大家都是知道Mysql是关系型数据库,用来存储数据的,在访问数据库量大的时候,Mysql读写磁盘访问的效率是非常低的,加上sql中的条件对数据的筛选过滤,那么效率就更低了。

这也是为什么引入非关系型数据库作为作为数据缓存原因,例如:Redis、MongoDB等,就是为了减少sql执行期间的数据库io操作。

同样的道理,若是每次执行update语句都要进行磁盘的io操作、以及数据的过滤筛选,小量的访问和数据量数据库还可以撑住,那么访问量一大以及数据量一大,这样数据库肯定顶不住。

基于上面的问题于是出现了redo log日志,redo log日志也叫做WAL威廉希尔官方网站 (Write- Ahead Logging),他是一种先写日志,并更新内存,最后再更新磁盘的威廉希尔官方网站 ,并且更新磁盘往往是在Mysql比较闲的时候,这样就大大减轻了Mysql的压力。

redo log的特点就是:redo log是固定大小,是物理日志,属于InnoDB引擎的,并且写redo log是环状写日志的形式:

如上图所示:若是四组的redo log文件,一组为1G的大小,那么四组就是4G的大小,其中write pos是记录当前的位置,有数据写入当前位置,那么write pos就会边写入边往后移。

而check point是擦除的位置,因为redo log是固定大小,所以当redo log满的时候,也就是write pos追上check point的时候,需要清除redo log的部分数据,清除的数据会被持久化到磁盘中,然后将check point向前移动。

redo log日志实现了即使在数据库出现异常宕机的时候,重启后之前的记录也不会丢失,这就是crash-safe能力。

binlog

binlog称为归档日志,是逻辑上的日志,它属于Mysql的Server层面的日志,记录着sql的原始逻辑,主要有两种模式,一个是statement格式记录的是原始的sql,而row格式则是记录行内容。

那么这样看来redo log和binlog虽然记录的形式、内容不同,但是这两者日志都能通过自己记录的内容恢复数据,那么为什么还要这两个日志同时存在呢?只要其中一个不就行了嘛,两个同时存在不就多此一举了嘛。且听我慢慢道来,这里面也大有文章。

因为刚开Mysql自带的引擎MyISAM就没有crash-safe功能的,并且在此之前Mysql还没有InnoDB引擎,Mysql自带的binlog日志只是用来归档日志的,所以InnoDB引擎也就通过自己redo log日志来实现crash-safe功能。

update执行过程

上面说了那么久两种日志的作用和特点,那么这两种日志究竟和update执行语句有什么关系呢?

先来看图:

前提:当前的引擎是使用InnoDB,update语句与select语句区别主要是这两日志的使用主要是在执行器和引擎之间进行交互时体现的区别。假如执行如下一条简单的更新语句是:

updateusersetage=age+1whereid =2;

上面说过select语句走过的流程update语句也会走一遍,当来到执行器的时候:

执行器会调用引擎的读接口,然后找到id=2的数据行,因为id是主键索引,索引按照树的搜索找到这一行,若是数据行已经存在于内存的数据页中就会立即将结果返回,若是不在内存中,就会从磁盘中进行加载到内存中,然后将查询的结果返回。

然后,执行器将返回的结果的age字段+1,并调用引擎的写接口写入更新后的数据行。

引擎获取到更新后的数据行更新到内存和redo log中,并告诉执行器可以随时提交事务,此时的redo log处于prepare阶段。

执行器收到引擎的告知后,生成binlog日志,并且调用引擎的接口提交事务,引擎将redo log的状态修改为commit状态,这样这个更新操作算是完成。

与select语句相比,因为select没有更新数据,只是将引擎查询的数据返回给执行器就算是完后,而update涉及数据的更新并且重新调用引擎接口写会存储引擎中的交互过程。

两阶段提交

上面详细的说了update语句的执行流程,提到了redo log的prepare和commit两个阶段,这就是两阶段提交,两阶段提交的目的是为了保证redo log日志与binlog日志保持数据的一致性。

若是redo log写成功binlog写失败,或者redo log写失败binlog写成功,最后使用这两者日志进行数据恢复得到的结果数据都是不一致性的,所以为了保证两个日志逻辑上的一致,使用两阶段进行提交。

redo log与binlog的总结

最后来对比一下这两种日志:redo是物理的,binlog是逻辑的,redo的大小固定,并且以环状的形式写入数据,数据满的时候需要将redo日志中擦除数据,并且将擦除的数据持久化到磁盘中。

而binlog以追加日志的形式写入,也就是当日志写到一定大小后,就会切换到下一个,并不会覆盖以前写的日志。

binlog是在Mysql的Server层中使用,因为binlog没有crash-safe功能,所以InnoDB引擎自己实现了redo log日志的crash-safe的功能,为了保证这两个日志逻辑上的一致使用两阶段提交。

在使用redo和binlog这两种日志的时候,可以将参数innodb_flush_log_at_trx_commit和sync_binlog都设置为1,它表示每次事务提交的时候,都会将日志持久化到磁盘中。

责任编辑:xj

原文标题:面试官:你知道 select 语句和 update 语句分别是怎么执行的吗?

文章出处:【微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MySQL
    +关注

    关注

    1

    文章

    804

    浏览量

    26542
  • SQL语句
    +关注

    关注

    0

    文章

    19

    浏览量

    7026
  • select
    +关注

    关注

    0

    文章

    28

    浏览量

    3912

原文标题:面试官:你知道 select 语句和 update 语句分别是怎么执行的吗?

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    深入理解C语言:循环语句的应用与优化技巧

    在程序设计中,我们常常需要重复执行某一段代码。为了提高效率和简化代码,循环语句应运而生。C语言作为一门经典的编程语言,提供了多种循环控制结构,帮助程序员高效地实现重复操作。掌握循环语句的使用,不仅
    的头像 发表于 12-07 01:11 113次阅读
    深入理解C语言:循环<b class='flag-5'>语句</b>的应用与优化技巧

    LTspice的编程语句应该怎么写?

    有关LTspice电路仿真软件的使用,应该怎么编写一个变压器的语句,尤其是多路输出的,并且这个软件是否支持多路浮动电源输出,就像反激式开关电源那样副边多路浮地? 有关LTspice的软件使用有什么资料推荐,重点是关于编程语句编写的,就像上面那样两个电感的耦合关系那样实现变
    发表于 10-11 19:19

    使用ChatGPT解决开发问题

    ) 和 sales_order_id(订单交付ID) 进行关联ORDER BYt.update_date DESC a.trans_type = '0'   原始语句解析:   主要用于获取销售订单和相关的交付信息#SELECT
    的头像 发表于 07-19 16:27 591次阅读
    使用ChatGPT解决开发问题

    如何限制IPD语句中的最大字节数?

    当数据可用时,我正在使用带有 IP 和 PORT 的扩展 IPD 语句。 我的问题是,如何限制 IPD 语句中的最大字节数? 当我尝试从服务器作为客户端检索数据时,响应包含大约 20k 的数据,在
    发表于 07-17 06:06

    STM8L执行一条语句大概需要几个时钟周期?

    STM8L执行一条语句大概需要几个时钟周期
    发表于 05-06 06:16

    鸿蒙TypeScript学习第7天:【TypeScript 循环】

    有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句执行,接着是第二个
    的头像 发表于 04-02 14:28 850次阅读
    鸿蒙TypeScript学习第7天:【TypeScript 循环】

    鸿蒙TypeScript入门学习第6天:【条件语句

    条件语句用于基于不同的条件来执行不同的动作。 TypeScript 条件语句是通过一条或多条语句执行结果(True 或 False)
    的头像 发表于 04-01 13:51 752次阅读
    鸿蒙TypeScript入门学习第6天:【条件<b class='flag-5'>语句</b>】

    stm32g030优化等级高于optimize for debug时,main函数里的while()循环出现if()内语句不能正常执行的原因?

    芯片使用G030,优化等级高于optimize for debug时,就会出现 main函数里的while()循环出现if()内语句不能正常执行的问题
    发表于 03-26 06:24

    使用Keil编译的时候else里的语句没有编译是怎么回事?

    最近遇到了一个编译问题,我在stm32f4xx_it.c文件中的CAN2_RX0_IRQHandler函数里编写了一个if——else if ——else语句,但调试的时候发现elseif
    发表于 03-11 06:42

    verilog中repeat必须用begin和end吗

    在Verilog中,repeat语句不需要使用begin和end块。repeat语句是一种循环控制语句,允许重复执行一个代码块指定的次数。它的一般语法如下: repeat (n) st
    的头像 发表于 02-23 10:14 1192次阅读

    assign语句和always语句的用法

    Assign语句和Always语句是在硬件描述语言(HDL)中常用的两种语句,用于对数字电路建模和设计。Assign语句用于连续赋值,而Always
    的头像 发表于 02-22 16:24 2535次阅读

    深入探讨嵌入式C编程的goto语句

    什么是goto语句? goto 语句被称为 C 语言中的跳转语句。 用于无条件跳转到其他标签。它将控制权转移到程序的其他部分。 goto 语句一般很少使用,因为它使程
    发表于 01-21 10:41 598次阅读
    深入探讨嵌入式C编程的goto<b class='flag-5'>语句</b>

    基于KEIL软件的C语言编程,如何计算一段程序的执行时间呢?

    如题,一段程序执行时间怎样确定,假如是把每条语句执行时间累加,那么每条语句的时间如何确定???有别的方法可以确定执行时间吗
    发表于 01-18 06:10

    单片机if是什么语句

    单片机中的if语句是一种条件语句,用于根据不同的条件执行不同的代码块。在程序执行过程中,条件语句用来决定是否
    的头像 发表于 01-05 14:04 1764次阅读

    单片机中for语句的运用

    单片机中的for语句是一种常见的循环控制结构,用于重复执行一段代码块,可以简化程序的编写和减少代码量。本文将详细介绍单片机中for语句的运用。 一、for语句的基本结构和功能 for
    的头像 发表于 01-05 14:02 2411次阅读