首先给大家上一张图,让大家能全局,直观的了解mysql基础架构:
我们可以将mysql分为两大层Server层
和存储引擎层
- Server层包括:连接器、查询缓存、分析器、优化器、执行器等
- 存储引擎:
负责数据的存储和提取
。而且是插件式的,我们可以更换选择不同的存储引擎。例如InnoDB或者MyISAM或Memory等等。我们在创建表的时候可以指定使用那种存储引擎。从mysql5.5.5版本开始,默认的存储引擎式InnoDB。也是我们平时用得最多的一种存储引擎。(存储引擎之间的区别在后面文章里讲)
1.连接器
连接器:负责和客户端建立连接,获取权限,维持和管理连接
连接命令示例:
mysql -h 127.0.0.1 -P 3306 -u root -p
- 上面的命令,其实就是我们通过客户端工具mysql去连接服务端,也就是建立连接。
- 连接建立成功后会提示我们输入密码,如果密码输入正确,连接器会去权限表查询你所拥有的权限。之后,这个连接里面的权限判断逻辑,都是依赖此时读到的权限。
这就意味着,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。
连接完成后,你可以通过命令show processlist;
查看连接,我们可以看到目前有两条连接,其中一条是sleep空闲状态,客户端如果太长时间没有动静,连接器就会掐断这个连接。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。
tips:希望大家能对默认的wait_timeout=8小时,这个参数比较敏感。如果遇到服务每隔一段时间连不上mysql,并且时间大概是8小时左右,可以往是不是连接超时,导致连接被干掉的原因。笔者是有遇到过相同的问题的。
长连接:连接成功后,如果客户端持续有请求,则一直使用同一个连接。
短连接:每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
我们尽量使用长连接,因为建立连接都是很耗资源的。这就好比我们开发过程中推荐使用连接池一样。
MySQL异常重启:全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM)
,从现象看就是 MySQL 异常重启了。
解决方案:
- 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
- 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
2.查询缓存
在执行一条查询语句时候,会首先到查询缓存看看,之前是不是执行过这条语句。(之前执行过的查询语句和结果会已key-value形式缓存在查询缓存中)。
- 如果客户端的本次查询能在查询缓存中找到对应的key,那么会直接给客户端返回对应的value结果。
- 如果不能在查询缓存中找到对应的key,那么就先执行后续一系列操作,从存储引擎中查到结果,然后再将查询语句和结果以key-value缓存到查询缓存。
查询缓存优点和缺点:
优点:后续的查询如果能命重缓存就能很大提高效率
缺点:查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。
结论:通常情况下并不建议使用查询缓存,查询缓存太容易失效,弊大于例。除非对于一些静态表,数据几乎不更新,可以使用。(Mysql8.0 废除了查询缓存功能)
3.分析器
客户端发起的SQL请求没有命重查询缓存,那么后面就要开始执行这条查询语句了。
首先,词法分析,分析这条语句具体是要干什么。例如:select * from A where id=1
,MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“A”识别成“表名 A”,把字符串“id”识别成“列 id”。
然后,语法分析,根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。
4.优化器
经过分析器后,Mysql就知道你要做什么了。在开始执行sql前,会先经过优化器处理,获得最优的执行方案。
5.执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做(最优方案),于是就进入了执行器阶段,开始执行语句。
1.开始执行时候,会先判断客户端是否拥有该表的权限,没有权限,就会返回没有权限错误。
2.如果有权限,那就打开表继续执行。执行器会去调用这张表存储引擎提供的接口,调用引擎接口获取到所有符合条件的数据。
例如select * from A where id=1
InnoDB引擎:
- 调用引擎接口获取表A的第一行,判断id是不是1,如果是就放入结果集中,如果不是就跳过;
- 调用接口获取下一行,重复相同逻辑到表最后一行
- 返回满足条件的结果集给客户端
对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。
tips:慢查日志中的rows_examined字段表示的是执行器调用存储引擎获取数据的次数;引擎扫描行数是引擎实际扫描了多少行。因此引擎扫描行数跟 rows_examined 并不是完全相同的