该文为《 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探索之旅,大家一起学习进步

MySQL 的一条语句是怎么执行的的更多相关文章

  1. MySQL:一条SQL是如何执行的

    目录 MySQL基本架构 Server层 连接器 查询缓存 分析器 优化器 执行器 存储引擎层 InnoDB MyISAM Memory SQL执行流程 MySQL基本架构 在讲SQL语句是如何执行之 ...

  2. 转载《mysql 一》:mysql的select查询语句内在逻辑执行顺序

    原文:http://www.jellythink.com/archives/924 我的抱怨 我一个搞应用开发的,非要会数据库,这不是专门的数据库开发人员干的事么?话说,小公司也没有数 据库开发人员这 ...

  3. 转 【MySQL】常用拼接语句 shell 下执行mysql 命令

    [MySQL]常用拼接语句 前言:在MySQL中 CONCAT ()函数用于将多个字符串连接成一个字符串,利用此函数我们可以将原来一步无法得到的sql拼接出来,在工作中也许会方便很多,下面主要介绍下几 ...

  4. python中a, b = a, a + b这条语句是如何执行的?

    a,b=b,a+b,这条语句在"理解"上还是与C语言有些差别的.在Python中,可以做下面的方式理解:首先,把等号右边的算式分别算完再说,然后按照一一对应的关系把值赋给等号左边的 ...

  5. 【417】一条语句编译并执行C语言

    参考:shell学习笔记(1)Linux下在一行执行多条命令 要实现在一行执行多条Linux命令,分三种情况: 1.&& 举例: lpr /tmp/t2 && rm / ...

  6. mysql实战45讲读书笔记(一) 一条SQL查询语句是如何执行的

    我们经常说,看一个事儿千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题.同样,对于MySQL的学习也是这样.平时我们使用数据库,看到的通常都是一个整体.比如,你有个最简单的表 ...

  7. 当程序执行一条查询语句时,MySQL内部到底发生了什么? (说一下 MySQL 执行一条查询语句的内部执行过程?

    先来个最基本的总结阐述,希望各位小伙伴认真的读一下,哈哈: 1)客户端(运行程序)先通过连接器连接到MySql服务器. 2)连接器通过数据库权限身份验证后,会先查询数据库缓存是否存在(之前执行过相同条 ...

  8. MySQL:一条更新语句是如何执行的

    目录 引言 更新流程图 更新流程说明 第一步:更新数据 数据页内存 Change Buffer 第二步:缓存日志内容 redo log buffer binlog cache 第三步:日志写入磁盘 两 ...

  9. MySQL数据库详解(一)执行SQL查询语句时,其底层到底经历了什么?

    一条SQL查询语句是如何执行的? 前言 ​ 大家好,我是WZY,今天我们学习下MySQL的基础框架,看一件事千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题.同样,对于MyS ...

随机推荐

  1. ReactNative---setState与性能的平衡

    setState用来更新RN的视图层显示,每一次setState操作都会更新整个 视图,于是对应的是性能消耗,在某些特殊情况下就会造成卡顿 app假死等问题: 因此个人使用setState中总结的原则 ...

  2. php--->底层的运行机制与数据结构原理

    PHP 底层的运行机制与数据结构原理 1. PHP的设计理念及特点 多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响,当然,随着时代发展,PHP也早 ...

  3. C++从array数组向vector向量复制元素的两种方式

    #include <iostream> #include <vector> using namespace std; int main() { const int arr_si ...

  4. C语言博客作业8

    本周作业头 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 作业地址 我在这个课程的目标是 熟练运用C语言来写代码 这个作业在那个具体方面帮助我实现目标 while语句的运用 参考文献 ...

  5. 浅谈DFS,BFS,IDFS,A*等算法

    搜索是编程的基础,是必须掌握的技能.--王主任 搜索分为盲目搜索和启发搜索 下面列举OI常用的盲目搜索: 1.dijkstra 2.SPFA 3.bfs 4.dfs 5.双向bfs 6.迭代加深搜索( ...

  6. 编译调试 .NET Core 5.0 Preview 并分析 Span 的实现原理

    很久没有写过 .NET Core 相关的文章了,目前关店在家休息所以有些时间写一篇新的

  7. cloud-init使用技巧

    对于 Linux 镜像,cloud-init 负责 instance 的初始化工作.cloud-init 功能很强大,能做很多事情,而且我们可以通过修改配置文件灵活定制 cloud-init. clo ...

  8. idea个人配置记录

    idea.properties # Use ${idea.home.path} macro to specify location relative to IDE installation home. ...

  9. OpenGL学习网址2

    http://www.opengpu.org/forum.php?mod=viewthread&tid=7525

  10. Go语言实现:【剑指offer】把数组排成最小的数

    该题目来源于牛客网<剑指offer>专题. 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字 ...