MySQL驱动
在程序和MySQL数据库进行通信前,需要先和数据库建立连接,这个功能主要是由MySQL驱动底层帮我们完成的,建立连接之后,我们只需要发送SQL语句就可以执行CRUD了
一次SQL请求就会建立一个连接,多个请求就会建立多个连接。当系统部署在tomcat容器中,tomcat可以并发处理多个请求,这会使得多个请求建立多个连接,然后使用完再进行关闭。Java程序与MySQL数据库进行连接的时候是基于TCP/IP协议的,如果有多个请求建立多个连接和销毁连接,频繁的创建和销毁连接会消耗大量资源,降低系统的性能;
因此就出现了连接池的概念,数据库连接池维护一定数量的连接线程,当需要使用连接时,就直接从连接池中获取,使用完毕之后,再把连接归还到池中。这样就减少了不断创建和销毁连接的开销,也不需要我们手动去创建连接和销毁连接。常用的连接池有:Druid、C3P0/DBCP;
MySQL架构的Server层
MySQL的整体架构:

Server层主要由连接器、查询缓存、解析器/分析器、优化器、执行器 几个部分组成的
连接器
客户端想要对数据库进行操作时,前提是与数据库建立好连接,而连接器就是用来负责跟可端建立连接、获取权限、维持和管理连接的;
- 连接方式
MySQL既支持短连接,也支持长连接。短连接就是操作完毕后,马上close关掉。长连接可以保持打开,减少服务端创建和释放连接的消耗,后续程序访问的时候还可以使用这个连接。
- 连接池
与客户端的连接池一样,为了减少频繁创建和销毁连接早晨的不必要的性能损失,这里也采用了“池化”的思想,通过数据库连接池去管理连接。一般会在连接池中使用长连接(druid、c3p0、dbcp)等
查询缓存
Mysql缓存是默认关闭的,不推荐使用缓存,在MYSQL8.0版本直接将查询缓存的整体功能删掉了;
- MySQL为什么默认不开启缓存?
受到使用场景的限制:
- 先说下缓存中数据存储格式:key(sql语句)-value(数据值),所以如果SQL语句(key)只要存在一点不同之处就会直接进行数据库查询了;
- 由于表中的数据不是一成不变的,大多数是经常变化的,而当数据库中的数据变化了,那么相应的与此表相关的缓存数据就需要移除掉;
分析/解析器
分析器的工作主要是对要执行的SQL语句进行解析,最终得到抽象语法树,然后再使用预处理器判断抽象语法数中的表是否存在,如果存在的话,在接着判断select投影列字段是否在表中存在等。
- 词法分析:词法分析用于将SQL拆解为不可再分的原子符号,称为token。根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。
- 语法分析:语法分析就是根据词法分析拆解出来的Token(原子符号)将SQL语句转换为抽象语法树。
- SELECT id,name FROM t_user WHERE status = ‘ACTIVE’ AND age > 18;对应的抽象语法树如下:

- 为了便于理解,抽象语法树中的关键字的Token用绿色表示,变量的Token用红色表示,灰色表示需要进一步分析。
- SELECT id,name FROM t_user WHERE status = ‘ACTIVE’ AND age > 18;对应的抽象语法树如下:
- 预处理器:预处理是用来对生成抽象语法树进行语义校验,语法校验就是对查询的表、select投影列字段进行校验、判断表、字段是否存在等。
优化器
优化器的作用主要是将SQL经过词法解析/语法解析后得到的语法树,通过MySQL的数据字典和统计信息的内容,经过一系列运算,最终得出一个执行计划,包括选择使用哪个索引。
在优化的过程中,经过一系列运算:
- 逻辑变换:例如SQL的where条件中存在8>9,那逻辑转换就是将语法数中存在的这种常量表达式直接进行化简,化简为false;除了化简还有常量表达式计算等。
- 代价优化:就是通过付出一些数据统计分析的代价,来得到这份SQL是否可以走索引,以及走哪些索引;除此之外,在多表关联查询中,确定最终表join的顺序等。【在分析是否走索引查询的时候,是通过进行动态数据采样统计分析出来,只要是统计分析出来的,那就可能会存在分析错误的情况,所以在SQL执行不走索引的时候,也要考虑到这方面的因素】
MqSQLD 执行计划,可以在SQL语句前添加explain关键字即可。
执行器
MySQL通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是进入了执行器阶段,开始执行语句。执行器最终就是根据一系列的执行计划去调用存储引擎提供的API接口去调用操作数据,完成SQL的执行。
开始执行的时候,要先判断一下建立连接的对象对这个表没有执行操作的权限,如果没有,就会返回没有权限的错误;如果有,就按照生成的执行计划进行执行。语法顺序
SELECT
- DISTINCT
(去重字段) - FROM
JOIN - ON
- WHERE
- GROUP BY
- HAVING
- ORDER BY
-
执行顺序
每个操作都会产生一张虚拟表,该虚拟表作为一个处理的输入。这些虚拟表对用户是透明的,只有最后一步生成虚拟表才会返回给用户。如果没有在查询中指定某一子句,则将跳过相应的步骤。
FROM<表名> 选取表,将多个表数据通过笛卡尔积变成一个表;
- 对FROM子句中的左表
和右表 执行笛卡尔积,产生虚拟表VT1。
- 对FROM子句中的左表
- ON<筛选条件> 对笛卡尔积的虚表进行筛选;
- 对虚拟表VT1应用ON筛选,只有那些符合
的行才被插入虚拟表VT2中。
- 对虚拟表VT1应用ON筛选,只有那些符合
- JOIN
,指定join,用于添加数据到on之后的虚表中,例如left join会将左表的剩余数据添加到虚表中。 - 如果指定了OUTER JOIN (如 LEFT OUTER JOIN、RIGHT OUTER JOIN),那么保留表中未匹配的行作为外部行添加到虚拟表TV2中,产生虚拟表TV3。如果FROM子句包含两个以上表,则对上一个连接生成的结果表VT3和下一个表重复执行之前的操作,直到处理完所有的表为止。
- WHERE
对上述虚表进行筛选 - 对虚拟表VT3应用WHERE过滤条件,只有符合
的记录才被插入虚拟表VT4中。
- 对虚拟表VT3应用WHERE过滤条件,只有符合
- GROUP BY<分组条件> 分组
- 根据GROUP BY子句中的列,对VT4的记录进行分组操作,产生VT5;
- SUM()等聚合函数,用HAVING子句进行判断。
- HAVING<分组筛选> 对分组后的结果进行聚合筛选;
- 对虚拟表VT5进行应用having过滤器,只有符合
的记录才会被插入虚拟表VT6中;
- 对虚拟表VT5进行应用having过滤器,只有符合
- SELECT <返回数据列表> 返回的单列必须在group by子句中,聚合函数除外。
- 第二次执行SELECT操作,选择指定的列,插入到虚拟表VT7中。
- DISTINCT 数据除重
- 去除重复数据,产生虚拟表VT8;
- ORDER BY <排序条件> 排序
- 将虚拟表VT8中的记录按照
进行排序操作,产生虚拟表VT9;
- 将虚拟表VT8中的记录按照
- LIMIT<行数限制>
- 取出指定行的记录,产生虚拟表VT10,返回给查询用户。
