MySQL-summaryOne
一条SQL查询语句的执行流程
我们先看一下,MySQL的基本示意图,并逐个解析每个部件的作用。
Server层
主要包含:连接器,查询缓存,分析器,优化器,执行器
这里提一嘴,查询缓存到MySQL8.0版本开始就被干掉了,具体原因往下看。
连接器:
负责客户端和MySQL建立连接,获取权限,维持和管理连接,一般通过mysql -h* -P* -u* -p
经过TCP三次握手协议之后,来连接上MySQL
- 如果用户账号密码不对,就会收到一个
Access denied for user
的错误,然后客户端结束执行。 - 如果用户账号密码正确,连接器就会在权限表中查出登录用户的权限,之后用户的所有相关操作都将依赖于刚刚所查询到的连接对应的权限。(如果管理员此时修改了你的权限,也不会影响到你现在的操作,除非重新登录创建连接)
当一个连接长时间处于sleep
状态的时候,默认(wait_timeout参数)8小时之后就会自动断开,连接又分为短连接和长连接,如果所有连接都是长连接,可能会导致内存占用太大,从而系统自动把长连接给OOM,一般表现就是MySQL的异常重启了。
原因分析:长连接会导致内存占用变大,MySQL在执行过程中临时使用的内存是管理在连接对象里面的,只有当这个连接断开的时候才会释放,所以都使用长连接会导致内存占用不断地变大,最终导致内存OOM,进而导致MySQL重启
查询缓存
连接建立完成,就可以根据select语句查询缓存,之前是否执行过这条语句,之前执行过的语句以及结果都是以key-value的形式直接缓存到内存中(到这里,希望读者和我一起想想这样可能会导致什么问题?结合前面提到的MySQL后面摒弃了查询缓存。),如果执行过,直接从缓存里面返回结果即可。
查询缓存其实往往弊大于利,原因如下:
- 查询缓存很容易失效,只要对一张表更新(比如更新某个字段属性),那么这个表上的所有缓存就会清空,所以会出现还没有二次使用就会被清空的情况。(除非你有一个很久都不会更新的静态表,比如系统配置表,那么适合查询缓存,但是平常的业务表,多少都带点经常更新的操作)。
如果要通过缓存查询,可以进行显式的语句查询,比如:select SQL_CACHE * from Table where user_id = 10;
,而且MySQL提供了参数query_cache_type
使得用户能自主控制是否使用查询缓存的操作。
分析器
如果没有命中缓存,就意味着开始真正执行语句了。
首先MySQL会进行“词法分析”,也就是MySQL会根据执行语句里面的字符串、空格等符号识别出分别代表什么。比如:select
代表这是一个查询语句,where
代表需要根据这个关键词后面的条件去查询,字符串user_id
代表是这张表的字段。
然后就是进行“语法分析”,MySQL会根据输入的语句判断这条语句是否符合语法,不符合就会返回一个“You have an error in your SQL syntax”的错误提示,比如elect * from user where user_id = 1;
这条语句,就是经典的语法错误,因为它的select
写错了
优化器
在开始执行前,MySQL还会对你的语句进行优化,确定这条语句走什么索引,或者连表查询的时候确定表的连接顺序,简而言之就是决定选择使用哪一套方案,当优化器阶段结束之后,那么这个语句的具体执行方案也就确定下来。
执行器
通过优化器的优化之后,进入执行器阶段,开始执行语句,这个时候会判断一下你有没有对目标表的执行权限,如果没有就会返回错误。
如果有权限就打开表执行,打开表的时候就会根据表的引擎定义,去使用这个引擎提供的接口对存储引擎进行相关操作。
比如一个查询语句select * from user where user_id = 25;
user_id`没有建立索引,执行流程如下:
- 调用InnoDB引擎(没有特指的话,MySQL默认使用InnoDB)接口取这个表的第一行,判断这行记录的
user_id
是不是25,如果不是那么就跳过,如果是就保存在结果集中 - 调用引擎接口取下一行,重复执行上述操作,直到取到这个表的最后一行
- 执行器将上述遍历过程中满足条件的结果集,写入内存并返回给客户端。
到此为止,一个查询语句的流程就走完了。一般情况下在慢查询日志中的rows_examined
字段记录的是这个执行语句扫描了多少行,当然有些场景下,引擎扫描次数跟rows_examined
并不是完全相同的。
存储引擎层
主要用来存储和提取数据,但是其架构模式是插件式,所以支持多种存储引擎,比如InnoDB、MyISAM等
小结
- 查询缓存在MySQL8.0被干掉了
- 一条查询语句执行流程分为两种
- 客户端 - 连接器 - 查询缓存(缓存命中) - 返回结果
- 客户端 - 连接器 - 查询缓存(缓存未命中) - 分析器 - 优化器 - 执行器 - 返回结果
- 连接器:客户端和MySQL连接,用户权限校验
- 查询缓存:当前的查询语句是否前面缓存过,如果缓存过,直接从缓存中拿结果返回,否则继续往下执行
- 分析器:进行词法和语法分析
- 优化器:确定执行器应该执行哪套优化后的方案
- 执行器:根据优化后的方案,调用对应引擎的接口,获取结果集并最终返回结果
问题:
- 优化器真的会每次都选最优的方案吗?如果不是,那么具体的确定方案流程是怎么样的呢?(比如索引确定,索引错选等)
- 如果执行
select * from user where user_id = 20
报错Unknown column user_id in where clause
在哪个阶段报出来的错误?
以上问题,会在后续的文章中一一解决回答,因为我也还在慢慢学习。
这些只是我临时思考的问题。如有侵权或文章内容错误,请各位大佬,私信或者联系邮箱xiaobazeo@gmail.com帮忙纠正。
你好,我是小八,一个分享日常学习知识总结的在校大学生。