MySQL 执行流程
客户端 ---TCP连接---> 连接器 -> 查询缓存 -> 词法分析 -> 语法分析 -> 语法树 -> 预处理器 -> 优化器 -> 执行计划 -> 执行器 -> 存储引擎
连接器
与 mysql 建立连接的过程?
通过
mysql -h$ip -u$user -p
连接 MySQL 服务。连接的过程需要先经过 TCP 三次握手。因为 MySQL 是基于 TCP 协议进行传输的。 完成 TCP 连接的建立后,连接器开始验证用户名和密码是否正确,如果用户名和密码没有问题,连接器会获取该用户的权限,然后保存起来, 后续在该用户在连接的任何操作,都会基于连接开始时读到的权限进行权限逻辑的判断。
这就解释了,为什么修改用户的权限之后,没有立即生效,因为权限的判断是基于创建连接时读取到的权限。如果想让权限立即生效,需要执行 flush privileges
.
或者重新登录即可获取到最新的权限。
空闲连接会一直占用着吗?
通过
show processlist;
可以查看哪些连接处于空闲状态。空闲连接达到一定的时间之后会自动断开。通过show variables like '%wait_timeout%';
来查看这个最大空闲时长。并且,这个连接断开时,客户端并不会第一时间直到,只有等待客户端发起下一次请求时,才会收到连接断开的报错。
MySQL 的连接数有限制吗?
有,通过
show variables like 'max_connections';
命令查看最大连接数。
mysql 的长连接和短连接
- 短连接
- 连接 mysql 服务 (TCP 三次握手)
- 执行 sql
- 断开 mysql 服务 (TCP 四次挥手)
- 长连接
- 连接 mysql 服务 (TCP 三次握手)
- 执行 sql
- 执行 sql
- ...
- 断开 mysql 服务 (TCP 四次挥手)
使用长连接的优缺点?
可以减少建立连接和断开连接的次数。 但使用长连接过多时,会导致 mysql 占用内存增多。 mysql 在执行查询过程中临时使用内存管理连接对象,这些连接对象资源只有在断开连接时才会释放。 如果长连接累计很多,将导致 mysql 服务占用内存很大,有可能被操作系统强制杀掉,这回导致 mysql 服务异常重启。
如何解决长连接的内存占用问题?
- 定期断开长连接。
- 客户端主动重置连接。 在代码里调用
mysql_reset_connection()
。这个过程不需要重新建立连接,会恢复到刚创建连接时的状态。
在建立连接时,连接器干了哪些工作?
- 与客户通过 TCP 三次握手,建立起 TCP 连接
- 检查用户名和密码
- 读取用户权限,后续在此连接的操作都基于此时读取到的权限。
查询缓存
MySQL 8.0 中以移除了缓存模块
客户端与 mysql 服务通过连接器建立连接后,客户端就可以向 mysql 服务 发生 sql 语句了。mysql 服务在收到 sql 语句后,就会解析出 sql 语句的第一个字段, 看看是什么类型的语句。如果是查询语句,就会先去查询缓存里查找缓存数据,看看之前有没有执行过这条语句。
查询缓存是以 key-value 形式保存在内存的,key 为 sql 查询语句,value 为 sql 查询结果。
如果命中缓存,就直接将 value 返回给客户端。没有命中就继续往下执行,等执行完后,将查询结果存入查询缓存。
解析 SQL
正式执行 sql 查询语句之前,mysql 会先对 sql 进行解析。这个过程由解析器来完成。
解析器
- 词法分析
- 语法分析,构建语法树。不会去检查表或者字段存不存在。
执行 SQL
- prepare 阶段,预处理阶段
- optimize 阶段,优化阶段
- execute 阶段,执行阶段。
预处理器
- 检查 sql 查询语句中的表或字段是否存在
- 将
SELECT *
中的*
扩展为所有列。
优化器
优化器主要负责将 sql 查询语句的执行计划确定下来。(例如由多个索引时,决定选择哪个索引。)
通过 explain
命令来查看 sql
的执行计划。查询结果中的 key
列表示使用的索引; possible_keys
表示可以使用的索引;
- key = null 表示全表扫描;
- key = PRIMARY 表示使用主键索引。
执行器
优化器确定了执行计划之后,mysql 的执行器就真正开始执行语句了。执行器会与存储引擎交互,交互是以记录为单位。
执行 sql 之前先判断下权限。
慢查询日志中的 rows_examined
字段表示这个语句执行过程中扫描了多少行,在执行器每次调用引擎获取数据行的时候累加;
执行器和存储引擎的交互
- 主键索引查询
- 全表扫描
- 索引下推
主键索引查询
全表扫描
索引下推
总结
简述下一条sql的执行过程
客户端通过 TCP 连接与 MySQL 服务建立连接,连接器在建立连接时会判断用户名和密码是否正确,如果不正确就返回异常。正确的话,会从数据库加载权限,
之后在此会话中执行的命令,都是基于此时读取到的权限。当客户端执行一条 sql 时,客户端通过之前建立的连接,sql 传给 MySQL
服务,mysql服务在接收到这条sql 后,会通过解析器,对这条 sql
进行词法解析和语法解析,生成一个语法树。解析无误之后,会通过预处理器来将 select * from ...
中 *
替换为具体的列名并判断sql
中涉及到的表和字段是否存在和判断权限。然后,优化器会根据之前生成的语法树,确定好执行计划。执行器会根据执行计划,来调用
MySQL引擎的接口来执行 sql ,执行完成之后,会将查询到的数据返回给客户端。
需要注意的是:mysql 8.0 之前解析器在对sql 解析之前,会先去缓存里面判断下有没有这条查询 sql 缓存结果。但是在 mysql8.0 之后,这个缓存模块被移除了。