MySQL 学习笔记 - 基本架构

135 阅读4分钟

在学习 MySQL 前我们先解剖一下 MySQL 的内部组成:

MySQL.png

可以看到,MySQL 其实分为两部分: Server 端和 存储端。

那么,当我们输入 select * from users where id = 1; 之后发生了什么?这个问题值得我们思考。

首先,我们需要和 MySQL 建立连接

连接器

我们可以在终端输入一条命令和 MySQL Server 建立连接

mysql -uroot -p

当我们输入完这个命令,连接器会校验用户名密码,如果输入正确会到权限表中找到该用户的所有权限并保存到一个变量中以供在这个连接中使用,如果此时对当前帐号权限进行调整并不会影响到已经建立的连接只会对新的连接生效,如果你希望老的连接也能够生效则需要重新建立连接。

如果建立完连接 8 小时(wait_time 默认时间)没有操作,连接会自动断开

ERROR 2013:Lost connection to MySQL server during query

我们可以设置更少的时间以便测试:

MySQL root@localhost:sys> set wait_timeout = 60;
Query OK, 0 rows affected
Time: 0.001s
MySQL root@localhost:sys> show variables like '%wait_timeout%'
+--------------------------+----------+
| Variable_name            | Value    |
+--------------------------+----------+
| innodb_lock_wait_timeout | 50       |
| lock_wait_timeout        | 31536000 |
| wait_timeout             | 60       |
+--------------------------+----------+
3 rows in set
Time: 0.008s
MySQL root@localhost:sys> show variables like '%wait_timeout%'
Reconnecting...
+--------------------------+----------+
| Variable_name            | Value    |
+--------------------------+----------+
| innodb_lock_wait_timeout | 50       |
| lock_wait_timeout        | 31536000 |
| wait_timeout             | 28800    |
+--------------------------+----------+
3 rows in set
Time: 0.009s

可以看到,在一分钟之后 MySQL 客户端在执行命令时进行了一次重连

查询缓存

适用场景:极少的数据变动(包括 DDL, DML),极高的固定查询

原理: 以 hash(key) 作为缓存 key,查询出的结果作为 value,在下一次查询时先到缓存中获取数据, 如果缓存中不存在数据会在执行完查询语句之后缓存结果,必须为完全一致的 key 才能命中缓存,不能有空格,大小写不一致等情况

参数设置:

query_cache_type: 0 代表不使用缓冲, 1 代表使用缓冲, 2 代表根据需要使用

mysql> select SQL_CACHE * from users where id=1; # 按需使用

不建议使用查询缓存,理由如下:

  • 一旦表数据进行任何一行的修改,基于该表相关 cache 立即全部失效
  • 当向某个表写入数据的时候,必须将这个表所有的缓存设置为失效,如果缓存空间很大,则消耗也会很大,可能使系统僵死一段时间,因为这个操作是靠全局锁操作来保护的

MySQL5.7 文档中表明查询缓存在 5.7.20 已弃用并在 8.0 移除 dev.mysql.com/doc/refman/…

Note: The query cache is deprecated as of MySQL 5.7.20, and is removed in MySQL 8.0.

分析器

你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL将会对字符串进行解剖,识别出这条 SQL 的具体含义

  • 词法分析 词法分析主要将输入的字符串转化为 Token, 其中 Token 包含关键字和非关键字, 例如:
select name from users

将被解析为:

关键字非关键字关键字非关键字
selectnamefromusers

我们可以在源码中看到关于关键字的定义, 这里有一部分摘录:

    {SYM("XML", XML_SYM)}, /* LOAD XML Arnold/Erik */

    {SYM("YEAR", YEAR_SYM)},

    {SYM("YEAR_MONTH", YEAR_MONTH_SYM)},

    {SYM("ZEROFILL", ZEROFILL_SYM)},

    {SYM("ZONE", ZONE_SYM)},

    {SYM("||", OR_OR_SYM)},

    /*

      Place keywords that accept optimizer hints below this comment.

    */

    {SYM_HK("DELETE", DELETE_SYM)},

    {SYM_HK("INSERT", INSERT_SYM)},

    {SYM_HK("REPLACE", REPLACE_SYM)},

    {SYM_HK("SELECT", SELECT_SYM)},

    {SYM_HK("UPDATE", UPDATE_SYM)},

相关源码部分: github.com/mysql/mysql…

入口函数: MYSQLlex

词法解析阶段出现的一些异常:

MySQL root@localhost:sys> select ad from users;
(1146, "Table 'sys.users' doesn't exist")
MySQL root@localhost:sys> select test from metrics;
(1054, "Unknown column 'test' in 'field list'")
  • 语法分析 语法分析实际上就是生成抽象语法树(AST)的过程, MySQL 使用了 Bison 实现, 语法分析器会按照语法规则判断你的 SQL 是否合法。
MySQL root@localhost:sys> select * frm metrics as;
(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 'frm metrics as' at line 1")

优化器

主要作用:

  1. 当存在多个索引时,决定使用的索引
  2. 使用 join 时决定各个表的连接顺序

执行器

执行语句之前先判断对当前表是否有操作权限,如果有权限就打开表去调用存储引擎接口获取数据,没有权限则返回错误信息

  1. 当使用索引时,调用 满足条件的第一行 接口, 然后循环调用 满足条件的下一行 接口,最后将所有结果组成结果集并返回给客户端
  2. 没有使用索引时,调用 该表第一行 接口,然后循环调用接口取下一行数据,直到该表的最后一行(全表扫描),最后将所有结果组成结果集并返回给客户端