当我们希望 MySQL 能够以更高的性能运行查询时, 最好的办法就是弄清楚 MySQL 是如何优化和执行查询的. 一旦理解了这一点, 很多查询优化工作实际上就是遵循一些原则让优化器能够按照预想的合理方式运行
当我们想向 MySQL 发送了一个请求时, MySQL 到底做了什么:
客户端首先发送一条查询请求给服务器
服务器首先检查查询缓存, 如果命中了缓存, 则立刻返回存储在缓存中的结果, 否则进入下一阶段.
服务器端进行 SQL 解析, 预处理, 再由优化器生成对应的执行计划
MySQL 根据优化器生成的执行计划, 调用存储引擎的 API 来执行查询
将结果返回客户端
MySQL 客户端 / 服务器通信协议
MySQL 客户端和服务器之间的通信协议是 "半双工" 的, 这意味着, 在任何一个时刻, 要么是由服务器向客户端发送数据, 要么是由客户端向服务器发送数据, 这两个动作不能同时发生. 所以, 我们无法也无需将一个消息切成小块独立来发送.
这种协议虽然让 MySQL 通信简单快速, 但是也从很多地方限制了 MySQL. 一个明显的限制是, 这意味着没法进行流量控制. 一旦一端开始传送信息, 另一端要接收完整个信息才能够响应它.
客户端使用一个单独的数据包将查询传给服务器, 当查询语句太长时, 服务端就会拒绝接受更多的数据并且抛出相应的错误. 因此, 当查询语句很长时, 参数 max_allowed_packet 就特别重要.
相反的, 一般服务器响应给用户的数据通常很多, 由多个数据包组成. 当服务器开始相应客户端请求时, 客户端必须完整的接受整个返回结果, 而不能简单的只取前几条结果.
换而言之, 当客户端从服务器获取数据时, MySQL 会一直向客户端推送数据, 客户端也没法让服务器停下来.
查询状态
对于一个 MySQL 连接或者说一个线程, 任何时刻都有一个状态, 该状态表示了 MySQL 当前正在做什么:
Sleep: 线程正在等待客户端发送新的请求
Query: 线程正在执行查询或者正在将结果发送给客户端
Locked: 在 MySQL 服务器层, 该线程正在等待表锁
Analyzing and statistics: 线程正在收集存储引擎的统计信息, 并生成查询的执行计划
Copying and tmp table [on disk]: 线程正在执行查询, 并将其结果集都复制到一个临时表中, 这种状态要么就是在做 GROUP BY 操作, 要么就是文件排序操作. 如果这个状态后面还有 on disk 标记, 那么表示 MySQL 正在将一个内存临时表存放在磁盘上
Sorting result: 线程正在对结果集进行排序
Sending data: 这表示多种情况: 线程可能在多个状态之间传送数据, 或者生成结果集, 或者在向客户端返回数据
查询缓存
在解析一个查询语句之前, 如果查询缓存是打开的, 那么 MySQL 会优先检查这个查询是否命中查询缓存中的数据, 这个检查是通过一个对大小写敏感的哈希查找实现的.
查询和缓存中的查询即使只有一个字节不同, 也不会匹配缓存结果. 这种情况下查询就会进入下一个阶段.
如果当前的查询恰好命中了查询缓存, 那么在返回查询结果之前 MySQL 会检查一次用户权限. 这仍然是无需解析查询 SQL 语句的, 因为在查询缓存中已经存放了当前查询所需要访问的表信息.
如果权限没有问题, MySQL 就会跳过所有其他阶段, 直接从缓存表中拿到结果并且返回给客户端. 在这种情况下, 查询不会被解析, 不用生成执行计划, 不会被执行.
查询优化处理
查询的生命周期的下一步是将一个 SQL 转换成一个执行接话, MySQL 再按照这个执行计划和存储引擎进行交互.
这包括多个子阶段: 解析 SQL, 预处理, 优化 SQL 执行接话.
这些过程中任何出错都可能终止查询.
查询执行引擎
在解析和优化阶段, MySQL 将会生成查询对应的执行接话, MySQL 的查询执行引擎则根据这个执行计划来完成整个查询. 这里的执行计划是一个数据结构, 而不是其他很多关系型数据库那样的字节码.
相对于查询优化阶段, 查询执行阶段并不那么复杂: MySQL 只是简单的根据执行计划给出的指令逐步执行. 在根据执行计划逐步执行的过程中, 有大量的操作需要通过调用存储引擎实现的接口来完成.
返回结果给客户端
查询执行的最后一个阶段是将结果返回给客户端. 及时查询不需要返回结果给客户端, MySQL 仍然会返回这个查询的信息, 如该查询影响到的行数.
在这个阶段中, 如果查询是可以被缓存的, 那么 MySQL 在这个阶段也将会被存放到查询缓存中.
MySQL 将结果集返回给客户端是一个增量地, 逐步返回的过程. 这样做有两个好处: 服务器端无需存储太多的结果, 也就不会因为要返回太多结果而消耗太多内存. 这样的处理也会让 MySQL 客户端第一时间获得返回的结果.
结果集中的每一行都会以一个满足 MySQL 客户端 / 服务器通信协议的封包发送, 再通过 TCP 协议进行传输, 在 TCP 传输中, 可能会对 MySQL 的封包进行缓存然后批量传输.
来源: http://www.linuxidc.com/Linux/2019-01/156328.htm