由于最近实在是太忙了,被溘然抽去做了一个重构项目,提及来大略,翻译代码,将别的团队的 php 逻辑翻译成 Java 的代码。
但是由于韶光紧,并且组织架构调度,熟习原来业务的同学比较少,只能一边熟习业务,一样平常翻译代码。
原来的项目的 php 代码(这里不关于措辞的谈论,只是说代码风格问题),没有任何设计可言,同一个逻辑到处重复,切实其实是搬运一堆垃圾。
加了许多班,熬了许多夜,根本没有韶光输出。
废话说完,下面进入正题,聊聊一个大略的 select 语句的实行过程。
select 之旅建立大略的用户表,表构造及测试数据如下
createtableuser(idintprimarykey,namevarchar(64))ENGINE=InnoDBDEFAULTCHARSET=utf8;insertintouservalues(1,"zhangsan");insertintouservalues(2,"lisi");insertintouservalues(3,"wangwu");
比如针对一个大略的用户表,如果想要查询 name = zhangsan 的用户信息,个中经由了哪些过程呢?
selectfromuserwherename='zhangsan';
msyql 基本架构
提及 查询过程还须要先容 mysql 的基本架构,整体架构如下:
个中紧张分为 Server 层和存储引擎层。
Server 层包括连接器、查询缓存、剖析器、优化器、实行器等。
mysql基本架构示意图
第一步:连接如果想要查询某个语句,首先得连接到 Mysql 做事器,这部分事情由 连接器完成,紧张事情包括:建立连接、获取权限、坚持和管理连接。
mysql-hip-pport-uusername-p
这里的连接是客户端与做事器之间的连接,也是一种TCP连接办法,同样要经由三次握手的过程,因此建立一次连接并不是一个大略的事儿,一样平常会设置一个比较合理的连接韶光,在该韶光范围内,纵然没有任何的查询动作还是保持客户端与数据库做事器的连接。
这个韶光是由参数 wait_timeout 掌握的,默认值是 8 小时。单位是s。可以通过如下命令查询该参数
showvariableslike'wait_timeout';
默认连接韶光
第二步:缓存可有可无的一步。
查询要求到来后,会先到查询缓存看看,之前是不是实行过这条语句。之前 实行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。
key 是查询 的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。
但是由于一旦对表做任何的更新操作,缓存都会失落效。一样平常评价缓存好坏的标准是什么?缓存命中率,比如 redis ,如果设置缓存命中率很低,这个缓存是尤其不合理的。
因此,一样平常情形下,不建议利用缓存,费劲的缓存起来数据,利用情形很少,对付性能提升,根本没有什么浸染。
如何判断数据库是否开启了缓存呢?
showvariableslike'query_cache_type';
我当前的 mysql 版本是 5.7.22,默认情形下,mysql 已经关闭了查询缓存。
查询缓存配置
第三步:剖析器如上图以是,我在剖析器右边写上了,语法剖析 + 词法剖析。
作甚词法剖析?
大概便是将 语句翻译成机器能够识别的措辞,识别出里面的字符串分别是什么,代表什么。
比如剖析出 select 表示查询,update 表示更新,把字符 串 “user” 识别成“表名 user”,把字符串 “ID” 识别成 “列 ID” 。
作甚词法剖析
那什么是语法剖析呢?
根据语法规则,判断你输入的这个 SQL 语句是否知足 MySQL 语法,就类似于英语中的语法一样。
还记得月朔的英语,当时对付一个大略的语句,比如
Iamaboy.
如果你写成
Iisaboy.
老师会见告你,你这样语法是缺点的。
对付 mysql 也一样,如果是输入如下查询语句,将 from 手误写成了 form,mysql 肯定也会提醒你的。
selectformuser;
语法缺点
如果下次你再见到类似的提示,不用看了,肯定是你哪个 sql 没有写对。
第四步:优化器根据名字就可以判断,这一步是 Mysql见告做事器,如何能够更加快速的实行语句。如果有多个索引,判断利用哪个索引,或者在一个语句有多表关联 (join) 的时候,决定各个表的连接顺序。
优化器阶段完成后,这个语句的实行方案就确定下来了,然后进入实行器阶段。
第五步:实行器听名字可以判断,这一步是一个没得灵魂的工具人角色,便是纯粹的实行语句。
还是回到最初的语句
selectfromuserwherename='zhangsan';
调用 InnoDB 引擎接口取这个表的第一行,判断 name 是否是zhangsan,如果不是则跳过,如 果是则将这行存在结果集中;调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的末了一行。实行器将上述遍历过程中所有知足条件的行组成的记录集作为结果集返回给客户端。
由于 name 上没有添加索引,以是会全表扫描所有的行,一行一行做判断,这一点我们可以通过慢查询日志看到踪迹
可以看到全体过程,扫描了3行,有一行知足哀求。
慢查询日志
如果 name 有索引呢?
这里我们新增加一个索引
ALTERTABLE`user`ADDINDEXname_index(`name`)
调用 InnoDB 引擎去name索引树查询 name = zhangsan,查找对应的主键值,得到id = 1,拿着主键id去 主键索引中查询 id=1 所在的行。重复相同的判断逻辑,直到取到这个表的末了一行。实行器将上述遍历过程中所有知足条件的行组成的记录集作为结果集返回给客户端。 可以看到加上索引之后,实在只是扫描了一行。 带索引的select
细我在测试的时候创造了故意思的事情,这里加了索引花费的查询韶光竟然比未加索引查询韶光更长。不是说加索引能够加快查询效率吗?
实在这里紧张是由于数据量太少了,而且加了 name 字段索引之后,还须要去主键索引树上查找某一行【这个过程为回表】,速率肯定比直接从三行中挑选一行更慢。
我这里将慢查询日志打开,并且设置慢查询日志的阈值为0s,便于不雅观察记录。
开启慢查询并阈值设置为0
总结这里紧张先容了 日常开拓中利用最多的 select 的查询过程,实在紧张是理解全体 mysql 的架构。
文中引出了索引的东西,实在这个观点大部分人并不陌生,但是可能对付 索引、主键索引、普通索引、回表等观点不是很熟习,这里只是大略先容,后续再和大家一起深入磋商。