Java客户端发起的select语句是如何执行的

267 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

在Java代码层面发起的select查询请求是如何执行的呢,掘友们你们知道吗?其实我之前的文章有简单的提到过,传送门:# MySQL下SQL的执行流程,今天会比较全面的讲述select查询的中间过程到底经历了哪些步骤。

当java客户端发起查询时,如下SQL:select * from class where className = '掘金课堂' 整体流程如下图:

image.png

1.连接器 首先会建立tcp连接,在命令行界面就是输入mysql -uroot -p xxxxpassword,如果用户名或者密码错误会抛出错误:Access denied for user 'rooot'@'localhost' (using password: YES),如果用户名密码都通过后,会去权限表里读取当前登录账户的权限,后续操作的权限就是这里获取的,这里我们也可以论证为什么我明明修改了某个已经登录账户的权限却发现当前登录账户还可以操作,那是因为权限在连接的时候就读取了,如果修改登录账户的权限应该使用命令刷新权限FLUSH PRIVILEGES;,当然重启MySQL也可以,连接完成后使用命令show processlist;就可以看到当前数据库的连接,如下图:

image.png

我们会发现MySQL连接即使一段时间不操作,比如10分钟,30分钟也不会关闭,那是因为MySQL连接是长连接,我们可以使用命令show variables like 'wait_timeout';查看当前数据库的默认超时时间,可以看到是28800秒也就是8小时,如下图:

image.png 建立完连接之后,如果是MySQL8.0以下的版本会去缓存,MySQL8.0将缓存废除了,MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果。如果你的查询能够直接在这个缓存中找到key,那么这个value就会被直接返回给客户端。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存 中。你可以看到,如果查询命中缓存,MySQL不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。对于缓存的介绍我在:# MySQL下SQL的执行流程这篇文章也说过,这里就不过多赘述。

2.分析器 如果没有缓存,那么MySQL需要知道你这个SQL需要干什么,首先需要进行词法解析和语法解析,对于词法解析和语法解析我也在:# MySQL下SQL的执行流程这篇文章也说过,这里就不过多赘述。

3.优化器 经过分析器生成的语法书,MySQL就知道你当前SQL是需要干什么了,比如是查询还是修改又或者是删除语句,查询的是哪些表,哪些字段,有哪些条件,但是此时SQL执行效率不一定是高的,这时候MySQL优化器就出来了,当前有如下SQL语句:select s.* from student s inner join class c on c.id = s.classId where c.id < 1000 and s.id < 5500000。 这里可以使用student表做驱动表也可以使用class表做驱动表,可以先判断c.id < 1000 也可以先判断s.id < 5500000,这里执行的先后顺序就是由优化器决定,也有可能将inner join 变为left join,将SQL的语法改变但是语义不变,也就是最终执行结果一样。

4.执行器 优化器之后会生成执行计划,此时就是MySQL真正意义上执行的SQL,在执行之前会根据建立连接时读取的权限判断当前账号是否有权限执行此SQL,如果当前账号是readOnly只读账户,那只能执行select语句,执行其他语句则会抛出错误SELECT command denied to user 'readOnly'@'localhost' for table 'student',如果有权限就会去内存数据页或者磁盘读取数据然后返回,也会将结果放入缓存。