首先我们来了解 MysqL 的逻辑架构,对 MysqL 有一个整体的认识。
例如在执行下面的一条语句时:
MysqL> select * from T where ID=10;
我们看到的结果是返回一条记录,那么 MysqL 是如何执行这条 sql 查询语句呢?
首先我们来看一下 MysqL 的逻辑架构图:
大体来说,MysqL 分为 Server 层和存储引擎层两部分。
Server 层包含连接器、查询缓存、分析器、优化器、执行器等 MysqL 的核心服务功能。
存储引擎层负责数据的存储和提取。其架构是插件式的,支持 InnoDB、MyISAM、Memory 等多种存储引擎。
从图中不难看出,不同的存储引擎共用一个 Server 层。下面我们通过上面那条 sql 查询语句来看一下整体流程和每个组件的作用。
连接器
连接器是客户端与 MysqL 进行连接的组件。客户端通过以下命令进行连接:
MysqL -h$ip -P$port -u$user -p
输完这条命令后,再输入用户名和密码。虽然也能够在上面的命令里同时输入密码,但容易造成密码泄露,尤其是在生产环境中,不建议把密码显式输入。
- 如果用户名或密码不正确,返回 "Access denied for user" 的错误,连接中断。
- 如果用户名密码认证通过,连接器会到权限表中查出你的权限,之后这个连接的权限判断都依赖于读取出来的权限。
这意味着,一个用户成功建立连接后,即便修改了他的权限,当前连接也不会生效,除非断开重连。
查询缓存
上一步与 MysqL 建立了连接,然后我们从客户端输入 sql 查询语句:
MysqL> select * from T where ID=10;
MysqL 会先到查询缓存里找,看是否存在这条记录。如果存在,则直接返回结果。
但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利。
查询缓存里的缓存数据很容易失效,只要表中任意数据的更新都会清空这个表的缓存,尤其是对于写比较频繁的应用,缓存基本不起效果。
我们可以通过把参数 query_cache_type
设置成 DEMAND
来禁用查询缓存,也可以显式设置使用查询缓存:
MysqL> select sql_CACHE * from T where ID=10;
注意:MysqL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。
分析器
通过分析器,MysqL 首先对 sql 语句进行词法分析,提取为若干个字符串。
然后再进行语法分析,判断 sql 语句是否符合 MysqL 的语法。
如果你的语句不对,就会收到 “You have an error in your sql Syntax” 的错误提醒,比如下面这个语句 select 少打了开头的字母 “s” 。
MysqL> elect * from t where ID=1;
ERROR 1064 (42000): You have an error in your sql Syntax; check the manual that corresponds to your MysqL server version for the right Syntax to use near 'elect * from t where ID=1' at line 1
一般语法错误会提示第一个出现错误的位置,所以你要关注的是紧接 “use near” 的内容。
优化器
经过分析器,MysqL 知道了要做什么,在开始执行前,还需要经过优化器的处理。
优化器是在表里有多个索引时,决定使用哪个索引。比如下面的查询语句:
MysqL> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
有两种执行方式:
- 先从表 t1 里读取
t1.c=10
的行,然后再从表 t2 里判断t1.ID=t2.ID and t2.d=20
。 - 先从表 t2 里读取
t2.d=20
的行,然后再从表 t1 里判断t1.ID=t2.ID and t1.c=10
。
优化器就是判断要选择哪种执行方式,大概的判断依据是 “成本估算”,估算哪种执行方式的 “成本” 更低,就选择哪种。
执行器
MysqL 经过分析器知道要做什么,经过优化器知道怎么做,最后就是执行了。首先 MysqL 会判断是否具有查询权限,如果没有,就返回没有权限的错误,如下:
MysqL> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有权限,就打开表继续执行。打开表的时候,执行器会根据表中定义的存储引擎,去使用这个存储引擎提供的接口。
比如我们的例子中,表 T 中,ID 字段没有索引,那么执行器的流程是:
- 调用 InnoDB 存储引擎接口取一行,判断
ID=10
。如果不是,则跳过;如是,则保存到结果集中。 - 然后再取下一行,重复直到表结束为止。
- 最后组成结果集返回给客户端。