总览
MySQL 逻辑架构整体上可以分为三层:
- 客户端:最上层的服务并不是MySQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
- Server层:大多数MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
- 存储引擎层:第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。Server层通过API与存储引擎进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的查询过程透明。
Server 层各个组件介绍
连接器
简介
连接器负责跟客户端建立连接、获取权限、维持和管理连接。
权限管理
服务器也会为安全接入的每个客户端验证它所具有的操作权限,这个连接之后的权限判断逻辑,都将依赖于此时读取到的权限。这就意味着用户成功连接后,即使管理员对该用户的权限做出了修改,也不会影响该连接的权限。修改完成之后,必须重新连接才会使用新的权限数据。
连接
MySQL在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程,同样在该层上可以实现基于SSL的安全链接。
客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时(28800 秒),如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。
数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。
但是全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。
上述问题解决的途径一般有两种:
- 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
- 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。
相关操作
# mysql 连接命令,-p 后面不建议直接填写密码,有泄露风险
mysql -h$ip -P$port -u$user -p
# 查看连接情况
show processlist
查询缓存
简介
查询缓存存储着MySQL 之前查询的结果(MySQL8.0 之后去除了这个组件),MySQL 在执行查询语句之前会先到查询缓存中查看,缓存中是否有本次查询的结果。
缓存
之前执行过的语句及其结果可能会以 key-value 对的形式,被直接存储在查询缓存中,key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。
但是实际上大多数情况下查询缓存往往弊大于利,查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务就是有一张静态表,很长时间才会更新一次。比如,一个系统配置表,那这张表上的查询才适合使用查询缓存。
按需使用缓存
好在 MySQL 也提供了这种“按需使用”的方式。你可以将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存。而对于你确定要使用查询缓存的语句,可以用 SQL_CACHE 显式指定,像下面这个语句一样:
select SQL_CACHE * from T where ID=10;
分析器
简介
分析器的作用是对 SQL 语句做词法分析、语法分析和语义分析。
词法分析
分析SQL 语句中的每一个单词是什么意思,例如 SELECT ID FROM USER; 词法分析会将 SELECT 识别出来,明白这是一个查询语句,USER 识别成 USER 表,ID 识别成 USER 表中的 ID列
语法分析
分析 SQL 语句中有没有语法错误,如果有错误会返回异常提示
SELEC * FROM USER
> 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELEC * FROM USER' at line 1
优化器
简介
优化SQL,提高语句执行效率(让SQL 语句以最高效率执行)
优化器可以在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。比如你执行下面这样的语句,这个语句是执行两个表的 join:
select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
- 既可以先从表 t1 里面取出 c=10 的记录的 ID 值,再根据 ID 值关联到表 t2,再判断 t2 里面 d 的值是否等于 20。
- 也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1,再判断 t1 里面 c 的值是否等于 10。
这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。
执行器
简介
执行器负责SQL 语句执行之前的 权限判断,调用存储引擎执行SQL 语句。
权限判断
MySQL 的权限判断逻辑之所以要在执行期阶段运行,是因为一条 SQL 语句要操作的表可能不止在语句中列出的那些(比如触发器)。有关权限判断,以后可能要专门学习一下,已知MySQL 的权限判断会在优化器执行之前执行一次 precheck。如果是命中了缓存,也需要在返回结果之前执行权限判断。
执行 SQL 语句
执行器会根据表的引擎定义,调用对应的引擎接口,存储引擎会返回对应的结果集。
一条SQL查询语句的执行顺序
- 客户端通过连接器连接 MySQL服务器,连接器做身份认证和权限信息收集
- 先根据SQL 语句查看查询缓存中是否存在缓存结果,存在的话判断权限,然后返回结果集,结束;不存在则执行下一步
- 分析器做词法、语法、语义分析,存在语法错误直接返回,结束;通过则执行下一步
- 优化器优化 SQL 语句执行效率
- 执行器做执行之前的权限判断,然后根据要操作的表的引擎定义调用对应存储引擎的接口执行SQL 语句
- 存储引擎返回结果集