MySQL 的一条语句是怎么执行的

该文为《 MySQL 实战 45 讲》的学习笔记,感谢查看,如有错误,欢迎指正

一、MySQL 的基础架构

以下就是 MySQL 的基础架构图。


在这里插入图片描述

在 Linux 中安装 MySQL 时,最少需要安装 mysql-server 以及 mysql-client,而服务端中又包含了 Server 层和存储引擎。

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

存储引擎层是独立的,可以理解为插件形式,Server 层接入了好几种存储引擎,比如MyISAMInnoDBMemory等,MySQL 5.5.5 之后默认的存储引擎是InnoDB。不管你的 MySQL 使用了多少种存储引擎,它们都是共享一个 Server 层。

如果在建表时(create table),想指定使用其它引擎,可以加上engine=MyISAM实现。


二、查询语句的执行过程

语句示例:

mysql> select * from T where id=10
2.1 连接器

连接器负责接收处理客户端发送过来的连接请求,获取权限,维持和管理连接。

客户端可以使用 mysql-client,通过命令行mysql -uroot -p进行建立连接。也可以使用第三方工具如Navicat建立连接。连接过程也是走的TCP/IP协议,有经典的3次握手过程。

连接器先判断用户名密码是否正确,不正确会返回Access denied for user错误;正确的话,会到权限表中查询出该用户的权限,只要该连接未断开,将会一直使用该权限。

这也就是为什么有时候我们即使修改了用户的权限,也不会立刻生效,必须断开重连,才能生效。当然如果该连接长时间处于空闲状态(连接上以后没有动作),默认 8 小时以后就会自动断开该连接。这个 8 小时是由wait_timeout来控制的。

通过show processlist可以查看哪些连接处于空闲状态,CommandSleep的就是空闲连接,可以通过kill Id来手动断开连接,一般在死锁或者事务阻塞的时候会用到。

mysql> show processlist;
+----+---------+-----------------+------+---------+------+-------+------------------+
| Id | User    | Host            | db   | Command | Time | State | Info             |
+----+---------+-----------------+------+---------+------+-------+------------------+
|  3 | root    | localhost:64511 | NULL | Query   |    0 | init  | show processlist |
|  4 | ruhrbim | localhost:64519 | NULL | Sleep   |    3 |       | NULL             |
+----+---------+-----------------+------+---------+------+-------+------------------+
2 rows in set (0.00 sec)

mysql>

MySQL 5.7及更新版本,可以使用mysql_reset_connection来初始化连接资源,这个操作不需要重新做权限验证,直接恢复到刚创建完连接时的状态。

2.2 查询缓存

查询缓存中存储的是之前的查询语句及结果,以key-value的形式存储,key是查询语句,value是查询结果。在执行select语句之前,会先去查询缓存看一下,如果有结果,直接从查询缓存返回,不经过后面的分析器、优化器、执行器等。如果没有结果,就会往后依次执行一遍,最后把语句及结果存入查询缓存,以便下次使用。

查询缓存主要针对的是变动不频繁的表,只要表发生了变更,那么这个表上的查询缓存都会被清空。MySQL也提供了"按需使用"的方法,将query_cache_type设置为DEMAND,这样默认 SQL 语句都不使用查询缓存,要使用的时候,可以通过SQL CACHE显式指定。

mysql> select SQL_CACHE * from T where ID=10;

Tips:MySQL 8.0 将查询缓存功能直接去掉了

2.3 分析器

没有命中查询缓存,就会到分析器这一步。分析器是对 SQL 语句做解析的。

首先进行「词法分析」,根据select判断是一个查询语句,还要把字符串T识别为表名 T,字符串ID识别为列 ID

然后进行「语法分析」,分析这一行 SQL 语句是否满足 MySQL 语法。

mysql> select * fron huanzi;
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 'fron huanzi' at line 1
mysql>

我们把from写成了fron,分析器做语法分析时识别出这里有问题,并将问题点定位出来了,在use near后面就是。

2.4 优化器

现在 MySQL 已经知道要做什么了,但在开始执行 SQL 语句之前还要经过优化器的处理。
优化器能够选择使用哪个索引,或者在多表关联的时候,选择连接的顺序。

当然有时候优化器也会选择错索引,我们可以使用force index(有索引的列名)来强制指定使用某一个索引。

mysql> select * from t force index(a) where a between 10000 and 20000;


2.5 执行器

SQL 语句经过了以上步骤,最终到达执行器,执行器的作用就是执行 SQL 语句。

开始执行的时候,要先判断一下是否有执行该语句的权限,没有权限会返回错误。

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

如果命中了查询缓存,不走执行器,也会在查询缓存返回结果的时候做权限验证。

如果有权限,就继续执行,以上述查询语句为例,执行流程如下:

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



三、更新语句的执行过程

给出一个表的建表语句:

mysql> create table T(ID int primary key, c int);

更新语句也有查询语句的那些流程,以update T set c=c+1 where ID=2;为例,首先连接到连接器,然后将表T上的缓存全部清空,分析器分析以后知道这是一个更新语句,优化器决定使用 ID 这个索引。执行器负责执行语句。

除了以上步骤,更新语句还多了 2 个日志模块,分别是redo log(重做日志)和binlog(归档日志)。

3.1 redo log

更新的时候,如果每次都要去磁盘找到那条记录,并且直接更新至磁盘,会产生很大的 IO 成本,在 MySQL 中有 1 个 WAL 技术就是为了解决这个问题,全称叫做 Write-Ahead Logging,关键点在于先写日志,再写磁盘。

具体来说,在更新数据库的时候,InnoDB 引擎会先把记录写到 redo log 里面,并更新内存,这时候更新就算已经完成了,InnoDB 引擎会在适当的时候,将记录更新到磁盘,一般是在服务器负载较低的时候。

InnoDB 里面的 redo log 是固定大小的,可以在/etc/my.cnf中进行配置,一般是 1 组 4 个文件,文件名是ib-logfile-0ib-logfile-1ib-logfile-2ib-logfile-3,会从 0 到 3 开始循环写,在 3 写满之后又会向 0 里面写,因此要永远保证 redo log 中有剩余空间可以记录信息,如果已经写满了,就会停下来先刷一部分数据到磁盘,空间腾出来以后,继续记录。

在这里插入图片描述

write pos 是当前记录的位置,checkpoint 是当前要擦除的位置。write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

3.2 binlog

redo log 是 InnoDB 独有的功能,在 MySQL 还没有引入 InnoDB 的时候,也有一个日志,就是 binlog 日志,主要功能是归档,以及主从复制使用。crash-safe 和 WAL 也都是 InnoDB 特有的。

  • binlog 是 Server 层的日志,并不是引擎层,因此所以的引擎都可以使用 binlog 日志。
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的,不会覆盖旧的文件。


    在这里插入图片描述

回到更新语句中,InnoDB 将数据写入 redo log 后还没结束,此时 redo log 处于 prepare 状态;

然后 InnoDB 告知执行器执行完成了,随时可以提交事务,执行器生成这个操作的 binlog,并把 binlog 写入磁盘。

执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

这个就是 MySQL 中的两阶段提交。


在这里插入图片描述

感谢阅读,有兴趣的小伙伴可以关注我的公众号DevOps探索之旅,大家一起学习进步

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270