该文为《 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. CSS中的定位体系

    一.概述     1.什么是定位体系     视觉格式化模型规定,定位体系共有三种             a.常规流(normal flow)             b.浮动(float)     ...

  2. 学_汇编语言_王爽版 要点采集笔记(未完待续…)

    第一章 基础知识 存储器(内存)存放CPU工作的指令和数据(CPU可以直接使用的信息在内存中存放):指令和数据都是二进制数没有任何区别,由CPU决定是数据还是指令 内存单元:存储器被分为若干个存储单元 ...

  3. JWT | io.jsonwebtoken.security.WeakKeyException: The signing key's size is 1024 bits which is not se

    背景 今天集成JWT的时候,选用了PS256算法,在用使用PGP KEY作为私钥JWT进行签名的时候,报了如下错误: "C:\Program Files\Java\jdk1.8.0_161\ ...

  4. Web 开发工具类(1): CookieUtils

    CookieUtils 整合了常用的一些对Cookie的相关操作: package com.evan.common.utils; import java.io.UnsupportedEncodingE ...

  5. 练习:等待用户输入input()

    等待用户输入 执行下面的程序在按回车键后就会等待用户输入: 实例(Python 3.0+) #!/usr/bin/python3 input("\n\n按下 enter 键后退出." ...

  6. 关于使用详解ASP.NET State Service

    ASP.NET State Service服务如果启动可以解决这个问题,它会生成一个aspnet_state.exe进程,这个就是Session信息的进程.只要这个进程在,就算是重启了IIS,站点的S ...

  7. socket实现文件上传(客户端向服务器端上传照片示例)

    本示例在对socket有了基本了解之后,可以实现基本的文件上传.首先先介绍一下目录结构,server_data文件夹是用来存放客户端上传的文件,client_data是模拟客户端文件夹(目的是为了测试 ...

  8. The 2019 University of Jordan Collegiate Programming Contest

    链接:https://codeforc.es/gym/102267 A. Picky Eater 直接比较 int main(){ int x ,y; scanf("%d %d" ...

  9. uniapp单页面配置无导航栏

    { "path": "pages/login/login", "style": { "navigationStyle": ...

  10. CVE-2019-0232:Apache Tomcat RCE复现

    CVE-2019-0232:Apache Tomcat RCE复现 0X00漏洞简介 该漏洞是由于Tomcat CGI将命令行参数传递给Windows程序的方式存在错误,使得CGIServlet被命令 ...