一条MySQL语句是怎样执行的​?​

📖 作者简介:在职Java架构师,分享Java、架构相关知识

📖 多年工作总结:Java学习路线总结,逆袭Java架构师

📖 技术交流:定期更新Java硬核干货,不定期送书活动、助你实现技术飞跃

📖 关注公众号【程序技术圈】,回复 面试题 ,获取《108道Java经典面试题总结(附答案)》pdf,复习方便,面试利器!

一条SQL的执行过程

在日常开发中,我们无论使用JDBC或者是可视化工具,执行SQL时,首先需要建立数据库连接,以JDBC为例,第一步加载jbcd驱动程序,接着建立连接,创建preparedStatement,执行SQL,最后处理返回结果,那么SQL在MySQL中做了什么操作呢?整个流程参考下面这张图

SQL执行流程

1.建立连接

下面开始SQL执行流程的第一步,建立连接,MySQL将连接器中的连接分为长连接和短连接。

  • 长连接是指连接成功后,客户端请求一直使用同一个连接

  • 短连接是指每次执行完SQL请求的操作之后会断开连接,如果再有SQL请求会重新建立连接。由于短连接会反复创建连接消耗相同资源,因此多数情况下会选择长连接。但是为了保持长连接,会占用系统内存

MySQL会在接收客户端的连接请求时,对客户端的身份权限进行检查,成功建立连接后才能继续往下进行,MySQL提供了一些连接参数供我们查看连接情况。

如何查看当前MySQL的连接(并发)数?

show global status like 'Thread%';

其中Thread_connected表示当前建立的连接数量,Threads_running表示当前运行着的线程数,大家可以多开几条连接,查看变化。

通过这行SQL,可以查看MySQL支持的最大连接数

show variables like 'max_connections';

需要调整最大并发数时,只需要改变这个值即可。

2.查询缓存

MySQL中存在一个缓存池,相当于将SQL作为key,查询的数据作为value保存在缓存池中,当下一次执行的SQL存在缓存池中时,直接取出缓存的数据返回,如果执行两次下面的语句,所需要的时间会一样吗?

select * from city where city_name = '杭州'

答案是不会的,因为MySQL默认缓存是关闭的,我们可以通过下面这条语句查看缓存开启情况

show variables like 'query_cache%'

MySQL为什么默认关闭缓存呢?主要是因为MySQL自带的缓存应用场景十分有限,首先它要求执行的SQL必须一模一样,就算中间有空格,或大小写不同都会导致缓存失效。另外,当表中的数据发生变化时,也会导致缓存失效。

这对于需要对大量数据进行更新操作的系统也不适用,所以缓存这块还是叫给数据库框架处理比较合适,例如Mybatis默认就开启了一级缓存,或者采用Redis等缓存中间件来实现更好一些,所以MySQL8.0也已经移除了缓存池。

MySQL如何判断缓存命中

再想一想,假设我们已经开启了缓存,再执行两次下面的SQL,第二次执行会不会命中缓存呢?

select * from city where create_date < CURRENT_DATE()

还是不会命中缓存,因为在包含一些不确定的函数时,不会被缓存,例如NOW()、CURRENT_DATE(),因为这些不确定的函数,使SQL变得不确定,另外,包含任何用户自定义的函数、存储过程、用户变量、临时表都不会被缓存。

如果希望带有一个日期的查询,那么最好将日期提前计算好

例如:

...DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY) -- 不缓存...DATE_SUB(‘2020-08-23’, INTERVAL 1 DAY) --缓存

3.语法解析和预处理

如果上一步没有命中缓存,那么在这一步会进行词法和语法的分析

例如这条语句

select name from city where id = 1;

首先会将这条语句切分成8个字符,再进行语法分析,例如引号有没有闭合,关键字是否存在等检查,然后再根据SQL语句生成一棵解析树

到这一步说明SQL已经通过检查,那么如果我们表名写错了呢?MySQL怎么进行检查?这就要靠下一步的预处理器来检查了。

预处理器会根据生成的解析树对表名、列名、别名等进行检查,确保不会出现歧义后重新生成一棵新的解析树。

4.查询优化执行计划

通过验证的SQL,由优化器将其转换为执行计划,一条查询可以有很多种执行方式,最后都返回相同的结果,而优化器的作用就是找到其中最好的执行计划。

举两个简单的例子:1、当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表。2、有多个索引可以使用的时候,选择哪个索引。实际上,对于每一种数据库来说,优化器的模块都是必不可少的,他们通过复杂的算法实现尽可能优化查询效率的目标。

如果我们想知道优化器是怎么工作的,它生成了几种执行计划,应该怎么做呢?首先,通过下面的语句开启优化器的追踪(默认是关闭的)

SHOW VARIABLES LIKE 'optimizer_trace';set optimizer_trace='enabled=on';

接着我们执行一个 SQL 语句,优化器会生成执行计划,执行下面的语句查看执行计划

select * from information_schema.optimizer_trace

这是一个包含三个部分的 JSON 数据,分别是准备阶段、优化阶段和执行阶段。其中,优化阶段生成了经过优化的 SQL 语句,保存在 expanded_query 字段中;执行阶段则包含了所有被考虑的执行计划,保存在 considered_execution_plans 字段中。

打开优化器追踪的开关对性能影响比较大,在分析完后记得关掉它

set optimizer_trace='enabled=off';

5.执行引擎

到这一步,MySQL会通过执行引擎调用存储引擎的API来查询结果,为什么我们切换了存储引擎,却不会影响执行引擎查询数据呢?原因是因为这些存储引擎都是用相同的API来向执行引擎提供操作的,查询出最终的结果后,把结果返回给客户端,客户端再根据需要对结果进行处理或者显示

📖 作者简介:在职Java架构师,分享Java、架构相关知识

📖 多年工作总结:Java学习路线总结,逆袭Java架构师

📖 技术交流:定期更新Java硬核干货,不定期送书活动、助你实现技术飞跃

📖 关注公众号【程序技术圈】,回复 面试题 ,获取《108道Java经典面试题总结(附答案)》pdf,复习方便,面试利器!

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值