本篇文章将经过一条 SQL 的执行过程来介绍 MySQL 的基础架构。mysql
首先有一个 user_info 表,表里有一个 id 字段,执行下面这条查询语句:spring
select * from user_info where id = 1;
返回结果为:sql
+----+----------+----------+--------+------+---------------------+---------------------+ | id | username | password | openid | role | create_time | update_time | +----+----------+----------+--------+------+---------------------+---------------------+ | 1 | 武培轩 | 123 | 1 | 1 | 2019-08-29 00:29:08 | 2019-08-29 00:29:08 | +----+----------+----------+--------+------+---------------------+---------------------+
下面给出 MySQL 的基本架构示意图,能够看出 SQL 语句在 MySQL 的各个模块中的执行过程。数据库
大致上,MySQL 分为 Server 层和存储引擎层两部分。缓存
Server 层包括链接器、查询缓存、分析器、执行器等,以及全部的内置函数(如日期、时间、数学和加密函数等)和跨存储引擎的功能(如存储过程、触发器、视图)。架构
存储引擎层负责数据的存储和提取,支持 InnoDB、MyISAM、Memory 等多个存储引擎。MySQL 5.5.5 版本后默认存储存储引擎是 InnoDB。函数
在查询 SQL 语句前,确定要先创建与 MySQL 的链接,这就是由链接器来完成的。链接器负责跟客户端创建链接、获取权限、维持和管理链接。链接命令为:优化
mysql -h$ip -P$port -u$user -p
输入密码,验证经过后,链接器会到权限表里面查出你拥有的权限,以后这个链接里面的权限判断逻辑,都将依赖于此时读到的权限,一个用户成功创建链接后,即便管理员对这个用户的权限作了修改,也不会影响已经存在链接的权限,修改完后,只有再新建的链接才会使用新的权限设置。加密
链接完成后,若是你没有后续的动做,这个链接就处于空闲状态,你能够在 show processlist
命令中看到它。结果以下:日志
+----+------+----------------+------------------+---------+------+----------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+----------------+------------------+---------+------+----------+------------------+ | 3 | root | localhost:2790 | NULL | Sleep | 5878 | | NULL | | 4 | root | localhost:2791 | springcloud_sell | Sleep | 5838 | | NULL | | 7 | root | localhost:2900 | springcloud_sell | Sleep | 5838 | | NULL | | 10 | root | localhost:3627 | springcloud_sell | Query | 0 | starting | show processlist | +----+------+----------------+------------------+---------+------+----------+------------------+
客户端若是太长时间没动静,链接器就会自动将它断开;这个时间是由参数 wait_timeout 控制的,默认值是8小时。若是在链接被断开以后,客户端再次发送请求的话,就会收到一个错误提醒:Lost connection to MySQL server during query
。
创建链接的过程一般是比较复杂的,建议在使用中要尽可能减小创建链接的动做,尽可能使用长链接。可是所有使用长链接后,有时候 MySQL 占用内存涨得特别快,这是由于 MySQL 在执行过程当中临时使用的内存是管理在链接对象里面的。这些资源会在链接断
开的时候才释放。因此若是长链接累积下来,可能致使内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。
怎么解决这个问题呢?能够考虑如下两种方案:
在创建链接后,就开始执行 select 语句了,执行前首先会查询缓存。
MySQL 拿到查询请求后,会先查询缓存,看是否是执行过这条语句。执行过的语句及其结果会以 key-value 对的形式保存在必定的内存区域中。key 是查询的语句,value 是查询的结果。若是你的查询可以直接在这个缓存中找到 key,那么这个
value 就会被直接返回给客户端。
若是语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。若是查询命中缓存,MySQL 不须要执行后面的复杂操做,就能够直接返回结果,会提高效率。
可是查询缓存的失效很是频繁,只要有对一个表的更新,这个表上全部的查询缓存都会被清空。对于更新压力大的数据库来讲,查询缓存的命中率会很是低。若是业务中须要有一张静态表,很长时间才会更新一次。好比,一个系统配置表,那这张表上的查询才适合使用查询缓存。MySQL 提供了这种按需使用的方式。能够将参数 query_cache_type 设置成 DEMAND,对于默认的 SQL 语句都将不使用查询缓存。而对于你肯定要使用查询缓存的语句,能够用 SQL_CACHE 显式指定,以下:
mysql> select SQL_CACHE * from user_info where id = 1;
MySQL 8.0 版本将查询缓存的功能删除了。
若是查询缓存未命中,就要开始执行语句了。首先,MySQL 须要对 SQL 语句进行解析。
分析器先会作词法分析。SQL 语句是由多个字符串和空格组成的,MySQL 须要识别出里面的字符串分别是什么,表明什么。MySQL 从你输入的 select 这个关键字识别出来,这是查询语句。它也要把字符串 user_info 识别成表名,把字符串 id 识别成列名。以后就要作语法分析。根据词法分析的结果,语法分析器会根据语法规则,判断输入的 SQL 语句是否知足 MySQL 语法。
若是你 SQL 语句不对,就会收到 You have an error in your SQL syntax
的错误提醒,好比下面这个语句 from 写成了 form。
mysql> select * form user_info where id = 1; 1064 - 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 'form user_info where id = 1' at line 1
通常语法错误会提示第一个出现错误的位置,因此要关注的是紧接 use near
的内容。
通过分析器的词法分析和语法分析后,还要通过优化器的处理。
优化器是在表里面有多个索引的时候,决定使用哪一个索引;或者在一个语句有多表关联(join)的时候,决定各个表的链接顺序。好比你执行下面这样的语句,这个语句是执行两个表的 join:
mysql> SELECT * FROM order_master JOIN order_detail USING (order_id) WHERE order_master.pay_status = 0 AND order_detail.detail_id = 1558963262141624521;
既能够先从表 order_master 里面取出 pay_status = 0 的记录的 order_id 值,再根据 order_id 值关联到表 order_detail,再判断 order_detail 里面 detail_id 的值是否等于 1558963262141624521。
也能够先从表 order_detail 里面取出 detail_id = 1558963262141624521 的记录的 order_id 值,再根据 order_id 值关联到 order_master,再判断 order_master 里面 pay_status 的值是否等于 0。
这两种执行方法的逻辑结果是同样的,可是执行的效率会有不一样,而优化器的做用就是决定选择使用哪个方案。优化器阶段完成后,这个语句的执行方案就肯定下来了,而后进入执行器阶段。
MySQL 经过分析器知道了要作什么,经过优化器知道了该怎么作,因而就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 user_info 有没有执行查询的权限,若是没有,就会返回没有权限的错误,以下所示 (若是命中查询缓存,会在查询缓存返回结果的时候,作权限验证。查询也会在优化器以前调用 precheck 验证权限)。
mysql> select * from user_info where id = 1; ERROR 1142 (42000): SELECT command denied to user 'wupx'@'localhost' for table 'user_info'
若是有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。好比咱们这个例子中的表 user_info 中,id 字段没有索引,那么执行器的执行流程是这样的:
对于有索引的表,第一次调用的是取知足条件的第一行这个接口,以后循环取知足条件的下一行这个接口。
数据库的慢查询日志中有 rows_examined 字段,表示这个语句执行过程当中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。在有些场景下,执行器调用一次,在引擎内部则扫描了多行,所以引擎扫描行数跟 rows_examined 并非彻底相同的。
主要经过对一个 SQL 语句完整执行过程进行讲解,介绍 MySQL 的逻辑架构,MySQL 主要包括链接器、查询缓存、分析器、优化器、执行器这几个模块。