MySQL深入01-查询语句
# 查询语句执行流程
首先我们来看一下MySQL
存在那些组件:
可以大致看出MySQL
一共分为两大部分
- Server层:包括连接器、查询缓存、分析器、优化器、执行器等,涵盖
MySQL
的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等 - 存储引擎:就是负责数据的存储读写,一般我们在开发中我们都是把引擎类型设置为
InnoDB
。不同的存储引擎使用的是同一个server
层
接下来我们依次对每个部分进行分析。
# 连接器
通常我们去链接数据库,我们会使用一下语句:
mysql -u账号 -p密码
这时候发挥作用的就是连接器
,连接器
开始对我们的身份进行验证:
- 账号或密码错误,我们就会收到一个错误信息。
- 账号和密码正确,就会去权限表里面查询到该账号所拥有的权限。
一旦链接成功之后,权限会一直跟着该链接走,哪怕在链接之后该用户的权限变化了,也不会影响到已经链接的权限,只有当用户重新链接之后,才会刷新权限。
现在我们可以通过show processlist;
来查询我们的链接:
可以看到Sleep
表示该链接处于空闲状态下,该链接就是空闲链接
。
客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。
而所谓的长链接
就是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
使用长链接
会存在一个问题那就是MySQL
内存占用特别快,这是因为 MySQL
在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。
解决方案:
- 定期断开
长连接
。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
# 查询缓存
查询缓存这个很容易理解,如果查询语句在查询缓存中,那么MySQL
会把上一次查询的结果保存下来,下次我们再查询的时候,如果还是相同的sql语句,就只需要从缓存中把对应sql语句的查询结果返回即可。
如果查询语句不在查询缓存中,就会继续后面的执行阶段,执行完成后,执行结果会被存入查询缓存中。
大多数情况下不要使用查询缓存:
查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。
设置不适用缓存查询:
可以将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存
对于特定的语句要使用缓存查询:
SELECT SQL_CACHE * FROM `sys_user` WHERE username = "admin";
需要注意的是,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。
# 分析器
如果没有命中查询缓存,那么从现在开始才真正执行我们的查询语句。
对于分析器,正所谓分析就是查看你所输入的sql语句是否正确,而分析器主要做两件事情:
- 词法分析 - 分析出输入的字符串分别是什么,代表什么。
- 语法分析 - 判断你输入的这个 SQL 语句是否满足
MySQL
语法。
如果表 T 中没有字段 k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。这个错误就是分析器阶段报出来的。
# 优化器
经过了分析器
阶段之后,MySQL
可以判断出你的SQL语句已经没有问题了,并且也知道你需要做的事情了,但是做一件事情有多种方案,这时候MySQL
还需要选择出最优的方案。
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。
# 执行器
MySQL
通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
开始执行的时候,会对你所查询的表进行权限判断,如果没有查询的权限,那么就会返回没有权限的错误。
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
对于我们的语句:
SELECT * FROM `sys_user` WHERE username = "admin";
- 调用
InnoDB
引擎接口取这个表的第一行,判断username是不是等于admin,如果不是就取下一行,如果是则将这一行数据保存在结果集中。 - 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
- 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
你会在数据库的慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。