mysql> select * from T where ID=10;
开门见山,当我们输入一条 SQL 语句的时候,MySQL 内部究竟执行了什么?直接上架构图,我们才能对其有一个概念,而不要陷入细节之中。
架构
大体上来说,MySQL 可以分为 Server 层与存储引擎两个部分。
所以不难看出,主要差别在于 Server 层,也就是连接器到执行器部分。接下来我们来说明上文的 SQL 语句到底在内部经历了什么。
第一步,客户端会先连接到数据库,这个时候就是连接器来接待。它负责跟客户端建立连接、获取权限、维持和管理连接。
如果用户名密码正确,连接器就到权限表查询你所拥有的的权限之后这个连接里面的权限判断,都依赖于此时读到的权限。
这就意味着,一个用户成功建立连接后,被修改了权限,也不会影响已经存在的连接的权限。修改完成后,只有再重新建立的连接才会使用到新的权限设置。
建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。
连接建立好了以后,就可以执行 select 语句了,执行逻辑进入第二步:查询缓存。
MySQL 拿到一个查询语句,会先查询缓存,先校验这个语句是否执行过,以 key-value 的形式存在内存里, Key 是查询预计,Value 是结果集。如果缓存 key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。
Mysql 查询不建议使用缓存,因为对于经常更新的数据来说,缓存的有效时间太短了,往往带来的效果并不好,对于不经常更新的数据来说,使用缓存还是可以的,Mysql 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。
如果没有命中缓存,那么就进入分析器,主要就是分析 SQL 语句是拿来干嘛,也就是解析该语句生成语法树,会分为两步:
经过了分析器分析,MySQL 知道你要干啥了,在开始执行之前,还要先经过优化器的处理。
优化器的作用就是它认为的最优的执行方案去执行(虽然有时候也不是最优),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
比如这条语句,既可以先从表 t1 里面取出 c=10 的记录的 ID 值,再根据 ID 值关联到表 t2,再判断 t2 里面 d 的值是否等于 20。也可以先从表 t2 里面取出 d=20 的记录的 ID 值,再根据 ID 值关联到 t1,再判断 t1 里面 c 的值是否等于 10。
这两种的执行逻辑结果是一样的,但是执行效率会有不同,而优化器就是决定使用哪种方案。
当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。这里的权限其实就是第一步客户端连接到连接器然后去查询出来的权限信息。
InnoDB 引擎更新数据的操作其实还会涉及到两个日志模块的操作,以后会专门分析。主要就是 binlog 以及 redolog 的操作。
InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态,更新完成。
《MySQL 专栏 45 讲》