一条查询SQL的执行过程



mysql 查看表当前正在执行 mysql查询正在执行的语句_查询缓存

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL5.5.5 版本开始成为了默认存储引擎。

从图中看,不同的存储引擎共用一个Server 层,也就是从连接器到执行器的部分。

连接器

连接器负责跟客户端建立连接、获取权限、维持和管理连接。

mysql -h$ip -P$port -u$user -p

在经历 TCP 握手后,连接器开始认证身份,这个时候用输入的用户名和密码,如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,结束执行。

如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。

权限表共有4个user, db, tables_priv, columns_priv

当用户通过权限验证,进行权限分配时,按照user, db, tables_priv, columns_priv的顺序进行分配。

先检查用户的全局权限表user,如果user中对应的权限为Y,则此用户对所有数据库的权限都为Y,将不再检查剩余3个表;如果为N,则到db表中检查此用户对应的具体数据库,并得到db中为Y的权限;如果db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y;如果为N,则到columns_priv中检查具体的列。

然后这个连接里面的权限判断逻辑,都将依赖于此时读到的权限「,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置」。

你可以在 show processlist 命令中看到它。Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。



mysql 查看表当前正在执行 mysql查询正在执行的语句_mysql 查看表当前正在执行_02


客户端如果太长时间没动静,连接器就会自动将它断开。 这个时间是由参数 wait_timeout

控制,默认值是 8 小时「可以通过 show global variables like 'wait_timeout'

来查看。 」。 如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。 这时候如果你要继续,就需要重连,然后再执行请求

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

长连接与短连接

所谓长连接,即用户的持续操作使用的都是同一个连接,连接在一段时间内长时间建立。

所谓短连接,即用户每做几次操作则断开,再下次操作时再进行连接。

长连接的优点是,在持续操作时,可以节省很多建立连接所需要消耗的时间。但是长连接所要存储的临时数据都在连接对象中,长时间积累,会导致系统内存溢出,具体表现 为Mysql异常重启。

短连接的优缺点与长连接相反,虽然不用担心内存溢出的问题,但短连接在持续操作的情况下多次连接,连接消耗很多时间,整体操作效率会很低。

怎么处理这个问题

1.定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。2.如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,会将连接恢复到刚刚创建完时的状态

查询缓存

连接器连接完成的下一步就是缓存器的缓存查询,如果我们需要对一张静态表(不常更新)经常做查询操作,那么可能会用到缓存器。

缓存器中使用的是key-value的存储形式,key值存储的是查询语句value值存储的是对应结果。要注意只要该表做了一次更新操作,那么该表对应的缓存就会全部被清理,因此使用场景并不多。

MySQL “按需使用”的方式。可以将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存。而对于你确定要使用查询缓存的语句,可以用 SQL_CACHE 显式指定,像下面这个语句一样:

select SQL_CACHE * from table_name;

所以当前缓存器的使用较少。我们可以通过query_cache_type来查看缓存器是否开启。



mysql 查看表当前正在执行 mysql查询正在执行的语句_缓存_03

MySQL 8.0 版本直接将查询缓存的整块功能删掉了

分析器

缓存没有命中那么连接器做连接操作之后就到分析器了。分析器做的事情就是对你输入的语句做 “词法分析” 与 “语法分析”

比如:SELECT age FROM user

“词法分析” ,就是判断每一个你输入的词,比如分析器首先会判断出你输入的第一个词是“SELECT”,第二个词你输入了“age”,等等。

“语法分析” 则是跟在 “词法分析” 之后,依据你输入的这些词来判断你输入的是否符合语法规则。假如符合语法规则,则会顺利进行下去并返回相应信息。

假如不符合语法规则,则分析器会返回报错信息给客户端。会看到一个“You have an error in your SQL syntax”的错误提醒,具体出错的地方,一般都是跟在use near之后

优化器

在分析器工作结束后,如果语法有问题,那么就会直接返回报错信息,且不继续向下运行。

语法正确,则会到优化器部分的工作。「对语句的执行做优化

比如,在一个语句查询某个表时,该表可能有多个索引,此时使用哪个索引会使语句的执行效率最高?这就是优化器要做的事情。

比如,执行语句select * from t1 join t2 on t1.ID=1 and t2.ID=2

该语句执行时,是先从t1表中找到ID=1的行关联到t2表之后,再从t2表中查找ID=2的行。还是先从t2表中找到ID=2的行关联到t1表之后,再从t1表中查找ID=1的行。

两种执行顺序可能就导致执行效率的不同,怎样选择执行顺序会提高执行效率,这也是优化器要做的事情

执行器

MySQL 通过分析器知道要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误

select * from T where ID=10;3 ERROR 1142 (42000): SELECT command denied to user '***'@'localhost' for table 'T'

如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限, 因为precheck是无法对运行时涉及到的表进行权限验证的,比如使用触发器的情况。因此在执行器这里也要做一次执行时的权限验证

如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

比如我们这个例子中的表 T 中,ID 字段没有索引,那么执行器的执行流程是这样的:

1.调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;2.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。3.执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。

会在数据库的慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值是在执行器每次调用引擎获取数据行的时候累加的。「在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟**rows_examined 并不是完全相同的」。

总结

一条SQL语句在Mysql中执行,一共会经历四步(算上连接Mysql),分别是连接、分析、优化与执行。

MySQL45讲[1]

References

[1] MySQL45讲: https://time.geekbang.org/column/intro/100020801