先上一张Mysql的基本架构示意图

Mysql的基本架构示意图
  1. Mysql分为Server层和存储引擎层两部分
  2. Server层:包含连接器,查询缓存,分析器,优化器,执行器,以及一些内置函数(日期,时间,数学和加密函数等),所有跨存储引擎的功能都在这层实现,例如存储过程,触发器,视图等
  3. 存储引擎层:负责数据的存储和提取,mysql5.5后默认的是InnoDB,还有MyISAM,Memory等
  4. 不同的存储引擎共用一个server层

一、连接器

负责和客户端建立连接,会经过TCP握手,获取权限,维持和管理连接。

如何进行连接呢?

mysql -h$ip -P$port -u$user -p

输入上面的连接命令后,会经过TCP握手之后,服务开始验证身份,账号密码正确,连接器回到权限表找到对应的账号权限,后面在这个连接器里进行的操作都会是基于这个权限的。

连接完成了呢?

  1. 如果没有后续动作,连接就会闲置下来,使用show processlist命令可以看到它。
  2. 客户端太久没有动静,连接器会自动断开,默认是8小时,这个时间参数是wait_timeout

长连接和短连接

  • 长连接:连接成功后,如果客户端持续有请求,则一直使用同一个连接
  • 短连接:每次执行完很少的几次查询后就断开连接,下次查询重新建立连接

如果全部使用长连接,Mysql内存占用会特别快,达到Out Of Memory,会触发Full GC,Mysql会异常重启。

解决方案:

  • 第一种方案:定期断开长连接,或者在执行完一个占用内存高的查询后,就断开连接,下次使用再重新连接
  • 第二种方案:可以使用mysql_reset_connection来重新初始化链接资源,这个过程是不需要重新连接的,但是会恢复到刚连接完的状态。

第二个方案会优雅一些。

二、查询缓存

查询缓存:连接建立完成后,我们执行sql语句,Mysql拿到这条SQL语句后,会到查询缓存里看看,之前是否执行过,之前执行的语句会以key-value的形式直接放到内存中。key是查询的语句,value是查询的结果。

但是大多数情况下,是不建议使用缓存的,为什么呢?

如果刚建立的缓存,还没使用,数据更新了,缓存就会被清空,重新写缓存,对于数据库的压力会更大,除非你的表是只用来查询的,都不更新数据

在Mysql8.0之后的版本,查询缓存的功能就被完全去掉了,所以其实可以不用考虑这个了。

三、分析器

如果没有命中缓存,就开始执行SQL语句了。

分析器就是对我们输入的SQL语句进行词法分析语法分析

比如输入

select * from Table1 where id=1

Mysql就会开始做词法分析,识别每个字符代表的是什么,哪些是关键字,哪些是表名,列名等,然后做语法分析,根据词法分析的结果,语法分析会判断你的sql的对错,错了就会提醒你哪里错了。

四、优化器

经过分析器,Mysql就知道你要干什么了,在开始执行之前,还要经过优化器的处理。

  1. 优化器在表里有多个索引的时候,会决定使用哪个索引
  2. 在一个语句有多表关联(join)的时候,决定各个表的连接顺序(连接顺序不同,效率会不一样)

五、执行器

通过了优化器,Mysql就知道怎么干了

1.判断当前连接对用到的表是否有相应的权限,没有,会返回无权限的错误。

2.如果有权限,就会打开表,继续执行。打开表的时候,执行器会根据表的使用的引擎定义,去使用这个引擎提供的接口。

select * from Table1 where id=1
  1. 调用InnoDB引擎接口获取这个表的第一行,判断ID是不是10,不是则跳过,是的话就将这行存在结果集中
  2. 调用引擎接口获取下一行,重复相同的判断逻辑,直到取到这个表的最后一行
  3. 执行器会将上述遍历过程中所有满足条件的行组成记录集作为结果集返回给客户端

我们在数据库的慢查询日志中看到的 rows_examined 字段,表示的是语句执行过程中扫描了多少行,这个值就是每次调用引擎获取数据时累加的。使用explain也可以看到执行计划,扫描了多少行。