无意之中看到了其他人博客写的一篇相关总结,阅读之后感觉受益良多。再次记录一下,以后需要的时候可以快速回忆。
记得有一次面试,我兴致勃勃的向面试官介绍我会什么JPA,mybatis,hibernate,mp…等等各种ORM框架。面试官:“好的,请你说一下一条SQL从后端发出到数据库执行完毕之后返回到后端中的整个流程。” 当时整个人都是裂开的。后来下来细想,确实学了不少框架,但是却连一个最基本的流程都没清清楚楚的搞明白。
正文
不管我们使用任何一中ORM框架,在使用之前我们需要引入这样的一个依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
这里已MySQL为例 在使用框架之前我们都需要引入对应的数据库驱动。通过这个驱动来连接数据库从而执行操作。
这个过程可能大概是这样,图片是摘抄自小道仙97这位博主,这篇博文也是看到他写的之后有感而发
但是实际运行的系统中却可能是这样
为了保证程序的高效运行,不可能采用单线程来处理任务。又因为频繁的获取数据库的连接是一个费时费力的活儿,所以各种数据库连接池的技术层出不穷,像我们比较熟知的大名鼎鼎的druid,c3p0,dbcp等等。同理数据库这边也不能只有一个线程来负责执行我们的操作。中间的省略了连接器对用户的身份和权限的校验。
后端发出的SQL语句到这正式被数据库SQL接口(可能这个说法不是太准确)接收到进行处理。
查询缓存
MySQL首先会向缓存中查询,如果查询到结果则直接返回。MySQL的缓存大概可以理解成为一个打的Map k是SQL语句 v是执行结果。所以相同的sql语句多了一个空格,就会造成缓存失效。缓存命中率低,所以在8.0的时候MySQL去除了这个组件。
分析器
SQL语句是人类可以理解的语言,这是一步是将SQL语句转换成MySQL可以理解的“语句”,让MySQL明白我们到底需要他去做什么事情。其中主要包括两个步骤,以这条SQL为例
select * from t where ID=9;
1.关键字分析:从这条SQL字符串中剔除空格,找到select,from ,where等关键字,并且把T识别成对应查询的表名,ID识别为对应的字段名称,如果ID字段不存在,这一步会返回错误代码1054 - Unknown column ‘id’ in ‘field list’ 。
2.语义分析:根据语义分析的结果判断SQL语句是否满足MySQL的语义规范,如果不规范会返回1064- You have an error in your SQL syntax 错误信息
优化器
经过上面的环节MySQL已经明白了自己要做什么,你以为这就可以执行了么?No,不能不明不白的去做事,必须要选择一个最优的方案去执行。
优化器可以决定在多表联查的时候各个表Join的顺序,存在多个索引的时候,使用哪个索引。以下面这条SQL为例
select * from t1 join t2 where t1.c=10 and t2.d=20;
这条SQL是执行两个表的join操作。我们有两种处理思路。
1 既可以从表t1取出c=10的记录的id值,再根据id值关联到表t2,再判断t2里面d的值是否等于20
2 也可以先从表t2取出d=20的记录的id值,再根据id值关联到表t1,再判断t1里面c的值是否等于10
最后的结果一样,但是具体执行的效率会不同,通常MySQL会比较t1和t2的大小来决定,通常是小表来驱动大表,即使你SQL里是t1 join t2 到这一步有可能会被MySQL 调换Join的顺序。
执行器
优化器生成了最优的执行计划之后就交给执行器去调用对应的存储引擎去执行SQL
buffer pool
在InnoDB引擎下,所有的操作都是先把数据读取到缓冲池(这个就是内存的一块区域),然后在这个区域内进行增删改查操作,然后再通过刷盘机制持久化到磁盘上。
通过上边我们不难看出SQL的执行流程大体如下:
session提交>SQL接口 > 查询缓存>分析器 > 优化器 > 执行器 > 调用引擎接口去执行>持久化