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