一, MySQL 逻辑架构
1.1 结构分析
整体来说, MySQL 的逻辑架构分成三个部分.
1) 客户端: 主要是建立连接的过程, 交互的过程
2) 核心服务
3) 存储引擎
这个可能比较抽象, 我们结合 MySQL 的查询过程, 结合着进行学习.
1.2 结构图
二, MySQL 查询过程
2.1 流程图
这个图其实就是在第一个的基础上, 进行的更加细致的划分, 因为上面只是大致画出了逻辑架构, 但是这个就展示了一整个过程.
2.2 过程分析
2.2.1 小结
1) 客户端向服务端发起一条请求
2) 服务端先检查查询缓存, 如果命中缓存, 则直接返回结果, 否则交给下一阶段
3) 服务器进行 SQL 解析, 预处理, 在经过查询优化形成对应的执行计划
4)MySQL 根据执行计划, 调用 API 给存储引擎, 进行数据的读取和存储
5) 将结果返回给客户端, 并缓存查询结果
大致整体的步骤就是这样的, 我们要把每一步都尽量的深入思考下, 我可能考虑的有欠缺, 欢迎大家进行留言
2.2.2 MySQL 客户端和服务器通讯
2.2.2.1 半双工通信机制
MySQL 客户端和服务器之间的通讯协议是 "半双工" 的, 这意味着, 在任何一个时刻, 要么由服务器向客户端发送数据, 要么由客户端向服务器发送数据, 这两个动作不能同时发生.
2.2.2.2 半双工通信机制优点和缺点
这种协议让 MySQL 通信简单快速, 但也限制了 MySQL. 一个明显的限制是, 这意味着没办法进行流量限制. 一旦一端开始发生消息, 另一端要接收完整个消息才能响应他.
2.2.2.3 执行分析
客户端用一个单独的数据包将查询传给服务器. 一旦客户端发送了请求, 它能做的事情就只是等待结果了. 一般服务器响应给用户的数据通常很多, 由多个数据包组成. 当服务器开始响应客户端请求时, 客户端必须完整的接受整个返回结果, 而不是简单的只收取前面几条结果, 然后让服务器停止发送数据. 所以, 使用 limit 子句去控制服务器发送给客户端数据的量. 这样可以提高性能.
如果没有 limit 进行限制的话, 查询出所有的数据都会发送给客户端, 比如我只需要 10 条. 但是没有限制, 假如取出了 100 条数据. 就会发送到客户端 100 条数据. 这其中其实有些是不需要的. 在这个过程中, 客户端是无法说: 我已经找到我需要的 10 条数据, 剩下的 90 条数据服务器请不要再发送了. 由于是半双工的通信机制, 那么你要做的只能等待服务器发送的 100 条数据全部发送完毕, 你才能进行操作. 所以没有 limit 进行限制后, 是不是增加了客户端的等待时间. 对性能有影响. 根据这种半双工的机制, 一般这样做: 客户端需要多少条数据, 我就在服务器操作的时候使用 limit 进行限制只取出多少条, 那么只会发送需要的条数给客户端.
而且, 多数连接 MySQL 的库函数都可以获得全部结果并缓存到内存里, 还可以逐行获取所需要的数据. 默认一般是获得全部结果并缓存到内存中. MySQL 通常需要等所有的数据都已经发送给客户端才能释放这条查询所占用的资源, 所以接受全部结果并缓存通常可以减少服务器的压力, 让查询能够早点结束, 早点释放对应的资源.
2.2.3 查询状态
对于 MySQL 连接, 任何时刻都有一个状态, 该状态表示了 MySQL 当前正在做什么. 使用 show full processlist 命令查看当前状态. 在一个查询生命周期中, 状态会变化很多次, 下面是这些状态的解释:
sleep: 线程正在等待客户端发送新的请求;
query: 线程正在执行查询或者正在将结果发送给客户端;
locked: 在 MySQL 服务器层, 该线程正在等待表锁. 在存储引擎级别实现的锁, 例如 InnoDB 的行锁, 并不会体现在线程状态中. 对于 MyISAM 来说这是一个比较典型的状态.
analyzing and statistics: 线程正在收集存储引擎的统计信息, 并生成查询的执行计划;
copying to tmp table: 线程在执行查询, 并且将其结果集复制到一个临时表中, 这种状态一般要么是做 group by 操作, 要么是文件排序操作, 或者 union 操作. 如果这个状态后面还有 on disk 标记, 那表示 MySQL 正在将一个内存临时表放到磁盘上.
sorting Result: 线程正在对结果集进行排序.
sending data: 线程可能在多个状态间传送数据, 或者在生成结果集, 或者在向客户端返回数据.
2.2.4 查询缓存
在解析一个查询语句之前, 如果查询缓存是打开的, 那么 MySQL 会优先检查这个查询是否命中查询缓存中的数据. 这个检查是通过一个对大小写敏感的哈希查找实现的. 查询和缓存中的查询即使只有一个字节不同, 那也不会匹配缓存结果, 这种情况下查询就会进入下一阶段的处理.
如果当前的查询恰好命中了查询缓存, 那么在返回查询结果之前 MySQL 会检查一次用户权限. 这仍然是无须解析查询 SQL 语句的, 因为在查询缓存中已经存放了当前查询需要访问的表信息. 如果权限没有问题, MySQL 会跳过所有其他阶段, 直接从缓存中拿到结果并返回给客户端. 这种情况下, 查询不会被解析, 不用生成执行计划, 不会被执行.
2.2.5 查询优化处理
查询的生命周期的下一步是将一个 SQL 转换成一个执行计划, MySQL 再依照这个执行计划和存储引擎进行交互. 这包含多个子阶段: 解析 SQL, 预处理, 优化 SQL 执行计划. 这个过程中任何错误都可能终止查询.
语法解析器和预处理: 首先 MySQL 通过关键字将 SQL 语句进行解析, 并生成一颗对应的 "解析树".MySQL 解析器将使用 MySQL 语法规则验证和解析查询; 预处理器则根据一些 MySQL 规则进一步检查解析数是否合法.
查询优化器: 当语法树被认为是合法的了, 并且由优化器将其转化成执行计划. 一条查询可以有很多种执行方式, 最后都返回相同的结果. 优化器的作用就是找到这其中最好的执行计划.
执行计划: MySQL 不会生成查询字节码来执行查询, MySQL 生成查询的一棵指令树, 然后通过存储引擎执行完成这棵指令树并返回结果. 最终的执行计划包含了重构查询的全部信息.
2.2.6 查询执行引擎
在解析和优化阶段, MySQL 将生成查询对应的执行计划, MySQL 的查询执行引擎则根据这个执行计划来完成整个查询. 这里执行计划是一个数据结构, 而不是和很多其他的关系型数据库那样对应的字节码.
MySQL 简单的根据执行计划给出的指令逐步执行. 在根据执行计划逐步执行的过程中, 有大量的操作需要通过调用存储引擎实现的接口来完成. 为了执行查询, MySQL 只需要重复执行计划中的各个操作, 直到完成所有的数据查询.
2.2.7 返回结果给客户端
查询执行的最后一个阶段是将结果返回给客户端. 即使查询不需要返回结果给客户端, MySQL 仍然会返回这个查询的一些信息, 如该查询影响到的行数. 如果查询可以被缓存, 那么 MySQL 在这个阶段也会将结果放到查询缓存中.
MySQL 将结果集返回客户端是一个增量, 逐步返回的过程. 这样有两个好处: 服务器端无须存储太多的结果, 也就不会因为返回太多结果而消耗太多的内存; 这样处理也让 msyql 客户端第一时间获得返回的结果.
结果集中的每一行都会以一个满足 MySQL 客户端 / 服务器通信协议的包发送, 再通过 tcp 协议进行传输, 在 tcp 传输的过程中, 可能对 MySQL 的封包进行缓存然后批量传输.
- https://blog.csdn.net/gao_yu_long/article/details/74905490
- https://www.cnblogs.com/kingle-study/p/9961748.html
来源: http://www.bubuko.com/infodetail-3808408.html