【MySQL 读书笔记】当我们在执行该查询语句的时候我们在干什么
看了非常多 MySQL 相关的书籍和文章,没有看到过如此优秀的专栏。所以未来一段时间我会梳理读完该专栏的所学所得。
当我们在执行该查询语句的时候我们在干什么
mysql> select * from T where ID=10;
让我们先来看一个架构示意图
MySQL 架构被清晰的分为了两层,服务器层 | 存储引擎层。
服务器层一般用于存放一些可以跨存储引擎执行的功能,Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),还有存储过程、触发器、视图等。
存储引擎层主要是负责数据的存储和提取,它是一个可插拔的状态。目前我们接触最多的肯定是 Innodb 了,我还使用过 PERCONA 的 Tokudb ,也是一个非常高性能支持事务的存储引擎。
下面和文章一样我们来走一遍执行该查询语句会经历哪些操作。
连接器
首先我们 MySQL 客户端,你可以理解为是我们常用于查询的 ORM 发起的连接到 MySQL 的连接器。连接器如图上标识的用于管理连接和进行权限验证。当我们连接上 MySQL 之后如果没有开始查询,我们的连接将会处于 Sleep 状态。如果我们太长时间没有进行任何查询,连接将会被 MySQL 断开。这个时候如果我们使用 ORM 进行请求就会报错了,需要我们重新连接,进行查询。
当然 MySQL 断开这个时间是通过参数控制的,这个参数是 wait_timeout 默认是 8 个小时
mysql> show variables like '%timeout%'; +-----------------------------+----------+ | Variable_name | Value | +-----------------------------+----------+ | connect_timeout | 10 | | delayed_insert_timeout | 300 | | have_statement_timeout | YES | | innodb_flush_log_at_timeout | 1 | | innodb_lock_wait_timeout | 50 | | innodb_rollback_on_timeout | OFF | | interactive_timeout | 28800 | | lock_wait_timeout | 31536000 | | net_read_timeout | 30 | | net_write_timeout | 60 | | rpl_stop_slave_timeout | 31536000 | | slave_net_timeout | 60 | | wait_timeout | 28800 | +-----------------------------+----------+
这里我多提一句,我在对比 MySQL 5.6 和 5.7 参数的时候发现多出来一个参数
have_statement_timeout 默认是 YES, 这个参数用于设置 select 查询语句的超时时间特性开启。
可以通过使用语句
56551 rows in set (0.11 sec) mysql> SELECT /*+ MAX_EXECUTION_TIME(100) */ * FROM ad_day_yxs1812_pay_detail; ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded
来设置一个最大的查询时间,如果查询时间超过这个时间则会报错。可以看到查询这56551 需要 0.11 sec 也就是 110 ms 我设置了 100ms 的查询时间所以没有办法得到结果就被打断了。
查询缓存
建立完连接之后就会走到查询缓存这一步,其实这可能是 MySQL 最没用的特性之一,查询缓存失效非常频繁,只要有数据发生更新,查询缓存就会失效。
我估摸着这个功能之前可能是用于缓存大量静态数据,但是现在都有 redis 之类的 NoSQL 来 handle 此类情况。所以我也想不到什么场景这个开启会比较有用,有个好消息是 MySQL 官方在 8.0 的时候彻底删除了该功能。
分析器
分析器负责把你的语句进行一些词法分析,如果你的语法有错误将会抛出错误进行提示。分析器处理完语句之后,MySQL 就知道该语句要做什么了。MySQL 的分析器部分分为 词法分析和语法规则。这两个将会生成一颗解析树提供给后面我们要讲到的优化器组件使用。
如果说我们执行
select * from T where k=1
然后没有 k 这一列就会被分析器这一层抛出错误。分析器会帮我们判断表和列是否存在。
优化器
优化器将根据上面的的的解析树生成执行计划。同时优化器还负责选择索引的事情,还有多表关联 join 的时候选择哪个表在前哪个表在后。
执行器
当优化器处理之后就到执行的时候了,开始执行的时候会判断我对该表的操作有没有权限,如果没有会返回权限错误。如果有权限就可以请求引擎的接口查询数据。
对于没有能使用索引的表执行:
1. 调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中
2. 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
对于有索引的表执行:
对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。
你会在数据库的慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。
在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的。
Reference:
本读书笔记皆来自发布在极客时间的 林晓斌(丁奇)的 MySQL 实战45讲:
极客时间版权所有: https://time.geekbang.org/ 版权所有:
https://time.geekbang.org/column/article/68319
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_have_statement_timeout
https://www.jiqizhixin.com/articles/2018-12-12-17 MySQL内核源码解读-SQL解析之解析器浅析