大家好,我是水滴~~
当希望 MySQL 能够以更高的性能运行查询时,最好的办法就是搞清楚 MySQL 是如何优化和执行查询的。一旦理解这一点,很多查询优化工作实际上就是遵循一些原则,让优化器按照预想的方式运行。
下图为 MySQL 执行一个查询的过程,我们一起看看它们的第一步都做的什么吧。
🌲 一、客户端/服务器通信协议
一般来说,我们不需要理解 MySQL 通信协议的内部细节,只需要大致理解通信协议是如何工作的。MySQL 客户端与服务端之间的通信协议是“半双工”的,这意味着,在任何一个时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器发送数据,这两个动作不能同时发生。
需要注意的一点是,当客户端从服务器取数据时,看起来是一个拉数据的过程,但实际上是 MySQL 服务器在向客户端推送数据的过程。
查询状态:对于一个 MySQL 连接,或者说一个线程,任何时刻只会有一个状态,该状态表示了 MySQL 当前正在做什么(可以通过
show full processlist
命令查看当前的状态)。
在一个查询的生命周期中,状态会变化很多次。下面是查询状态的列表:
- Sleep:线程正在等待客户端发送新的请求。
- Query:线程正在执行查询或者正在将结果发送给客户端。
- Locked:在 MySQL 服务器端,该线程正在等待表锁。
- Analyzing and statistics:线程正在收集存储引擎的统计信息,并生成查询的执行计划。
- Copying to tmp table [on disk]:线程正在执行查询,并且将结果集都复制到一个临时表中(例如:执行
group by
、文件排序或union
操作)。如果这个状态后面还有“on disk”标记,那表示 MySQL 正在将一个内存中的临时表拷贝到磁盘上。 - Sorting result:线程正在对结果集进行排序。
- Sending data:这表示多种情况,线程可能在多个状态之间传送数据,或者在生成结果集,或者在向客户端返回数据。
了解这些状态的基本含义非常有用,这可以让你很快地了解当前查询执行的状态。若某个地方有了异常,也能很快地诊断出问题来。
🌲 二、查询缓存
在解析一个查询语句之前,如果查询缓存是打开的,那么 MySQL 会优先检查这个查询是否命中查询缓存中的数据。这个检查是通过一个对大小写敏感的哈希表实现的,如果没有匹配出缓存结果,那么查询会进入下一阶段的处理。
如果当前查询恰好命中了查询缓存,那么 MySQL 会直接从缓存中拿到结果并返回给客户端(在返回结果之前,MySQL 会检查一次用户权限,权限没问题会直接返回)。
🌲 三、查询优化处理
查询的生命周期的下一步,是将一个 SQL 转换成一个执行计划,这个过程称之为查询优化处理。这包括了多个子阶段:解析 SQL、预处理、优化 SQL 执行计划。
1. 语法解析器和预处理
首先,MySQL 通过关键字将 SQL 语句进行解析,并生成一棵对应的“解析树”。MySQL 解析器将使用请求规则验证和解析查询。
预处理则根据一些 MySQL 规则进一步检查解析树是否合法。例如,检查数据表和数据列是否存在、名称和别名是否有歧义等。下一步,预处理器还会验证权限。
2. 查询优化器
如果语法树是合法的,那么优化器会将其转化成执行计划。一条查询可以有很多种执行方式,它们的返回结果是相同的。优化器的作用就是找到这其中最好的执行计划。
3. 执行计划
和很多其他关系数据库不同,MySQL 并不会生成查询字节码来执行查询。MySQL 生成查询的一棵指令树,然后通过存储引擎执行完成这棵指令树并返回结果。最终的执行计划包含了重构查询的全部信息。
🌲 四、查询执行引擎
执行计划生成后,MySQL 的查询执行引擎则根据这个执行计划来完成整个查询。
相对于查询优化阶段,查询执行阶段不是那么复杂:MySQL 只是简单地根据执行计划给出的指令逐步执行。在该过程中,有大量的操作需要通过调用存储引擎实现的接口来完成,这些接口称为“handler API”。
为了执行查询,MySQL 只需要重复执行计划中的各个操作,直到完成所有的数据查询。
🌲 五、返回结果给客户端
查询执行的最后一个阶段是将结果返回给客户端。即使查询不需要返回结果集给客户端,MySQL 仍然会返回这个查询的一些信息,如该查询影响的行数。
如果查询可以被缓存,那么 MySQL 在这个阶段也会将结果存放到查询缓存中。
MySQL 将结果集返回给客户端是一个增量、逐步返回的过程。结果集中的每一行都会以一个满足 MySQL 客户端/服务器通信协议的封包发送,再通过 TCP 协议进行传输,在 TCP 传输过程中,可能对 MySQL 的封包进行缓存,然后指传输。