MySQL一般分为两层,一层是Server层,一层是存储引擎层。Server层提供连接分析优化执行等大多数的和兴功能,存储引擎层则负责数据的存储和提取,其架构模式是插件式,支持InnoDB、MyISAM等多个存储引擎。
连接器
负责和客户端建立连接、获取权限、维持和管理连接。
命令:mysql -h$ip -P#port -u$user -p,执行后要求输入密码。
如果用户名和密码不对,则会收到‘Access denied for user’;
如果用户名和密码通过,连接器会到权限表里面查出用户名拥有的权限,之后的这个连接里的权限判断逻辑,都将依赖于这次读到的权限。
通过上面可以看出,一个用户建立连接后,及时管理员账号对这个用户的权限做了修改,也不会影响已存在连接的权限。修改完成后,只有新建立的连接才会使用新的权限设置。
连接建立后,如果没有后续动作,这个连接就处于空闲状态,用show processlist命令可以看到连接的Command状态为Sleep。
客户端如果太长时间没有动静,连接器就会自动将这个连接断开。时间的长短由‘wait_timeout’控制,默认8小时。
如果连接被断开后,客户端再次发送请求的话,就会收到‘Lost connection to MySQL server during query’。
长连接和短连接
长连接是指连接建立成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询后,就断开连接,下次查询再重新建立一个。建立连接的过程通常是比较复杂的,除了tcp连接外还有权限验证等,所以要尽量减少建立连接的动作,也就是尽量使用长连接。
长连接的问题
全部使用长连接后,可能MySQL占用的内存涨得特别快,这是因为MySQL在执行过程中使用的内存是管理在连接对象里面的,所以这些内存资源会在连接断开时才释放。如果长连接积累下来,可能导致内存占用的太大,最终系统被强行杀掉OOM,从现象看就是MySQL异常重启了。
解决长连接的问题
1)定期断开长连接
2)MySQL5.7版本后,可以在每次执行完一个比较大的操作后,通过执行mysql_rest_connection来重新初始化连接资源。这个过程不需要重连和重新权限验证,就可以把连接会恢复到刚创建时的状态。
查询缓存
连接建立后,查询SQL执行的第二步就是‘查询缓存’。
缓存会将执行语句和执行结果以key-value的形式保存在内存中。查询时如果命中key,则将value直接返回给客户端。
不过多数的生产环境下,不建议使用缓存查询。因为查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都被清空,这样对于频繁更新的数据库来说,查询缓存的明中路会非常低,维护成本又很大。只有静态表,像配置表,才适合使用查询缓存。
MySQL可以对单条SQL使用查询缓存。MySQL8.0之后,将查询缓存整块功能删除。
分析器
如果没有命中查询缓存,则开始真正的执行语句了。MySQL需要知道你要做什么,因此需要对SQL语句做解析。
分析器会先做‘词法分析’,分析出SQL语句里面的字符串,通过select判断这是一个查询语句。然后再做‘语法分析’,根据语法规则,判断语句是否满足MySQL语法。
如果语句不对,则会收到‘You have an error in your SQL syntax’的错误 ,一般会提醒第一个错误的位置。
优化器
经过分析器后,MySQL就知道你要做什么了。再开始执行之前,还要先经过优化器的处理。
优化器的作用是:表中包含多个索引时,决定使用哪一个索引;语句有多表关联查询时,决定各个表的连接顺序。详细的优化过程,后面会单独讲解。
执行器
通过分析器知道了要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
执行器开始执行时,会判断用户对要查询的表有没有执行查询的权限,如果没有就返回没有权限的错误。权限校验具体是在查询命中后返回结果前,或者在优化器之前调用precheck验证权限。
如果有权限,就会打开表继续执行。执行器会根据当前表的引擎定义,使用这个引擎提供的接口。
执行过程
对于没有索引的查询:
1)调用InnoDB引擎接口取表的第一行,判断where条件是否符合,如果不符合则跳过,如果符合则将这行存在结果集中。
2)调用引擎接口读取下一行,重复相同的判断,直到表的最后一行。
3)执行器将上述遍历过程中的所有满足条件的行,组成的记录集合,作为结果返回给客户端。
对于有索引的表,大致类似:
调用引擎的‘满足条件的第一行’接口,之后循环调用‘满足条件的下一行’接口,得到记录集合。
数据库的慢SQL日志中有一行rows_examined字段,表示语句执行过程中扫描了多少行,这个数值就职执行器每次调用引擎获取数据航的时候累加得到的,不过有时执行器调用一次,在引擎内部扫描了多行时,引擎扫描行数就和rows_examined不完全相等了。
