乐趣区

关于java:一条简单select经过了多少路程

扯闲篇

本篇开始之前,先来扯一些闲篇,举例上一次更新过来了靠近两个月,说好了一周两更,为啥始终没有更新技术文章呢?

因为最近切实是太忙了,被忽然抽去做了一个重构我的项目,说起来简略,翻译代码,将别的团队的 php 逻辑翻译成 Java 的代码。

然而因为工夫紧,并且组织架构调整,相熟原来业务的同学比拟少,只能一边熟悉业务,个别翻译代码。

原来的我的项目的 php 代码(这里不对于语言的探讨,只是说代码格调问题),没有任何设计可言,同一个逻辑到处反复,几乎是搬运一堆垃圾。

加了许多班,熬了许多夜,基本没有工夫输入。

<img src=”https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210704104626.jpeg” alt=”” style=”zoom:50%;” />

废话说完,上面进入正题,聊聊一个简略的 select 语句的执行过程。

select 之旅

建设简略的用户表,表构造及测试数据如下

create table user (
  id int primary key,
  name varchar(64) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into user values (1, "zhangsan");
insert into user values (2, "lisi");
insert into user values (3, "wangwu");

比方针对一个简略的用户表,如果想要查问 name = zhangsan 的用户信息,其中通过了哪些过程呢?

select * from user where name = 'zhangsan';

msyql 根本架构

说起 查问过程还须要介绍 mysql 的根本架构,整体架构如下:

其中次要分为 Server 层和存储引擎层。

Server 层包含连接器、查问缓存、分析器、优化器、执行器等。

第一步:连贯

如果想要查问某个语句,首先得连贯到 Mysql 服务器,这部分工作由 连接器实现,次要工作包含:建设连贯、获取权限、维持和治理连贯。

mysql -hip -pport -uusername -p

这里的连贯是客户端与服务器之间的连贯,也是一种 TCP 连贯形式,同样要通过三次握手的过程,因而建设一次连贯并不是一个简略的事儿,个别会设置一个比拟正当的连接时间,在该工夫范畴内,即便没有任何的查问动作还是放弃客户端与数据库服务器的连贯。

这个工夫是由参数 wait_timeout 管制的,默认值是 8 小时。单位是 s。能够通过如下命令查问该参数

show variables like 'wait_timeout';

第二步:缓存

可有可无的一步。

查问申请到来后,会先到查问缓存看看,之前是不是执行过这条语句。之前 执行过的语句及其后果可能会以 key-value 对的模式,被间接缓存在 内存中

key 是查问 的语句,value 是查问的后果。如果你的查问可能间接在这个缓存中找到 key,那么这个 value 就会被间接返回给客户端。

然而因为一旦对表做任何的更新操作,缓存都会生效。个别评估缓存好坏的规范是什么?缓存命中率,比方 redis,如果设置缓存命中率很低,这个缓存是尤其不合理的。

因而,个别状况下,不倡议应用缓存,吃力的缓存起来数据,应用状况很少,对于性能晋升,基本没有什么作用。

如何判断数据库是否开启了缓存呢?

show variables like 'query_cache_type';

我以后的 mysql 版本是 5.7.22,默认状况下,mysql 曾经敞开了查问缓存。

第三步:分析器

如上图所以,我在分析器左边写上了,语法分析 + 词法剖析。

何为词法剖析?

大略就是将 语句翻译成机器可能辨认的语言,辨认出外面的字符串别离是什么,代表什么。

比方剖析出 select 示意查问,update 示意更新,把字符 串“user”辨认成“表名 user”,把字符串“ID”辨认成“列 ID”。

那什么是语法分析呢?

依据语法规定,判断你输出的这个 SQL 语句是否满足 MySQL 语法,就相似于英语中的语法一样。

还记得初一的英语,过后对于一个简略的语句,比方

I am a boy.

如果你写成

I is a boy.

老师会通知你,你这样语法是谬误的。

对于 mysql 也一样,如果是输出如下查问语句,将 from 手误写成了 form,mysql 必定也会揭示你的。

select * form user;

如果下次你再见到相似的提醒,不必看了,必定是你哪个 sql 没有写对。

第四步:优化器

依据名字就能够判断,这一步是 Mysql 通知服务器,如何可能更加疾速的执行语句。如果有多个索引,判断应用哪个索引,或者在一个语句有多表关联 (join) 的时候,决定各个表的连贯程序。

优化器阶段实现后,这个语句的执行计划就确定下来了,而后进入执行器阶段。

第五步:执行器

听名字能够判断,这一步是一个没得灵魂的工具人角色,就是纯正的执行语句。

还是回到最后的语句

select * from user where name = 'zhangsan';
  1. 调用 InnoDB 引擎接口取这个表的第一行,判断 name 是否是 zhangsan,如果不是则跳过,如 果是则将这行存在后果集中;
  2. 调用引擎接口取“下一行”,反复雷同的判断逻辑,直到取到这个表的最初一行。
  3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为后果集返回给客户端。

因为 name 上没有增加索引,所以会全表扫描所有的行,一行一行做判断,这一点咱们能够通过慢查问日志看到形迹

能够看到整个过程,扫描了 3 行,有一行满足要求。

如果 name 有索引呢?

这里咱们新减少一个索引

ALTER TABLE `user` ADD INDEX name_index (`name`) 
  1. 调用 InnoDB 引擎去 name 索引树查问 name = zhangsan,查找对应的主键值,失去 id = 1,拿着主键 id 去 主键索引中查问 id=1 所在的行。
  2. 反复雷同的判断逻辑,直到取到这个表的最初一行。
  3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为后果集返回给客户端。

能够看到加上索引之后,其实只是扫描了一行。

我在测试的时候发现了有意思的事件,这里加了索引破费的查问工夫居然比未加索引查问工夫更长。不是说加索引可能放慢查问效率吗?

其实这里次要是因为数据量太少了,而且加了 name 字段索引之后,还须要去主键索引树上查找某一行【这个过程为回表】,速度必定比间接从三行中筛选一行更慢。

我这里将慢查问日志关上,并且设置慢查问日志的阈值为 0s,便于察看记录。

总结

这里次要介绍了 日常开发中应用最多的 select 的查问过程,其实次要是理解整个 mysql 的架构。

文中引出了索引的货色,其实这个概念大部分人并不生疏,然而可能对于 索引、主键索引、一般索引、回表等概念不是很相熟,这里只是简略介绍,后续再和大家一起深入探讨。

退出移动版