前言
宿舍里,小A兴致满满的将刚写好的查询SQL执行流程
文章通过微信发给小美,回想起昨晚透过窗户意外看到小美哭的梨花带雨,问过她室友才知道竟然是因为面试被SQL执行流程给难住了。心里暗自下决心:作为小美背后的男人,我绝不允许!
另一边小美收到备注为备胎N号-小A发的消息下意识就要关掉,却模糊间看到了查询SQL执行流程
几个关键字。想起被面试官吊打的情景,按耐不住打开了文章。发现根本停不下来,忍不住两眼直冒金光。
正文开始
MySQL 是我们工作中必不可少的技术栈,怎么都避不开它。在使用 MySQL 时,我们最经常接触的就是DML语句,但是一条DML语句是如何执行的你了解嘛?接下来我们就以查询语句为例,详细解析它在 MySQL 中的执行流程。
上图是一条查询SQL的主要流程,简单概述下:
-
同 MySQL 建立连接,并进行权限认证和校验
-
根据 SQL 语句查询缓存,命中则直接返回
-
未命中则对 SQL 进行词法、语法解析,异常则抛出错误
-
生成执行计划,进行索引选择(不一定是最优解)
-
执行器操作存储引擎执行 SQL 语句,返回结果
可以看出主要包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心功能。而存储引擎负责数据的存储和提取,支持 InnoDB、MyISAM、Memory 等多个存储引擎。
连接器
第一步就需要与连接器打交道,一般通过以下命令建立链接:
mysql -h ip -P port -u user -p password
账号和密码校验通过后,会到权限表中查出用户所拥有的权限。后续的权限校验都会据此来作出判断,相当于一个权限快照,后续的权限修改不影响已成功建立连接的用户。
可以通过 SHOW PROCESSLIST;
命令查看当前连接,如果连接长时间处于 Sleep,到达 wait_timeout(默认8小时),连接器会自动将连接主动断开
断开连接后请求就会收到一个错误提醒,需要重新建立连接。而如果客户端持续有请求 MySQL 则会重复使用同一个连接(长连接)。不能每次执行完几条 SQL 语句就断开连接,下次查询再重新建立(短连接)。建立连接的过程比较复杂,持续使用短连接会影响系统性能。在实际开发过程中,我们都会使用连接池来维护连接,完成 SQL 语句执行。
使用长连接可能会遇到MySQL内存飞涨(临时使用的内存是管理在连接对象中,会在连接断开时才释放);如果长连接过多,会导致内存占用过大,被系统强制 Kill 掉(Out Of Memory),导致 MySQL 发生异常重启。通常有两种解决方式:
-
定期断开长连接,在系统执行完一个较大内存查询后
-
如果当前 MySQL 版本大于等于5.7,可以通过通过
mysql_reset_connection
重新初始化连接资源。不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
查询缓存
建立连接后,Mysql 接收到客户端的查询语句,会先查询缓存。如果该查询语句之前执行过(需要语句完全一致),则其结果可能被缓存下来,后续命中缓存的情况下可以直接返回给客户端。
看上去查询效率很高,但在实际使用中通常会关闭该功能(8.0版本后删除了该功能)。因为缓存失效概率非常大,只需要对表数据做更新就会导致该表的所有缓存都失效。可通过命令将 query_cache_type 设置成 DEMAND,关闭该功能。
分析器
进入到分析器后,接下来才是真正进入执行 SQL 流程。
首先会进行词法分析,一条 SQL 语句通常由很多个字符串、空格拼接而成,会把字符串中的关键字、表名、字段名等拆解出来。
进行语法分析时,将一条 SQL 语句生成一个数据结构,这个数据结构我们把它叫做解析树(select_lex)。接着判断其语法规则是否符合,如果服务器进程认为SQL语句不符合语法规则的时候,就会把这个错误信息反馈给客户端。
最后进行语义解析,判断对应的表名、字段名、视图等等是否在数据库中存在,不存在就直接报错给用户。
优化器
优化器主要是对当前执行语句进行优化,得到当前形势下最有效的执行路径,生成该语句的执行计划。
往往实际运用中,一个表会存在多个索引。每个 SQL 语句可能满足多个索引的使用规则,优化器是在表里面有多个索引的时候,决定使用哪个索引或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
现在默认使用的是CBO(成本优化器),从目标诸多的执行路径中选择一个成本相对最小的执行路径来作为执行计划。这里的成本是基于查询语句使用不同索引时对当前系统的IO、CPU等消耗来进行估算,从而选择一个成本最低的执行计划。
执行计划
到达最后执行时,会先做用户的权限校验,判断是否有当前表、列等数据的查询权限。
权限没问题就会调用存储引擎提供的接口查询数据。例如用InnoDB引擎接口去扫描这个表的第一行,判断职业为程序员,如果不是则跳过,如果是则将这行存在结果集中 调用引擎接口取下一行,重复相同的逻辑判断,直到取到这个表的最后一行。
最后执行器将满足条件的所有数据汇总成一个结果集返回给客户端。
总结
小美看完内心忍不住感慨,小A平时看着呆头呆脑的,还是有点东西啊!以后要多跟他交流,面试官还不得对我刮目相看。(默默将备胎N号-小A升级为备胎一号-小A)
希望对你也有帮助,SQL 执行流程是面试经常被问的问题,看完这篇文章妈妈再也不用担心面试官问你这题了。如果大家对 MySQL 其他问题感兴趣或者面试被问到回答不好的(也不局限于 MySQL,可以是 Java 各方面),可以留言或私信,根据反馈持续更新。