听说你写sql很溜,一条sql查询语句是如何执行的?

我们项目中必不可少要与数据库接触,那么一条sql查询语句是如何执行的呢?

本文以MySQL数据库为例。MySQL是典型的C/S架构(client客户端/server服务端),客户端进程向服务端进程发送一段指令,服务端进程进行语句处理然后响应执行结果。

问题来了。服务端进程对客户端发送的请求究竟做了什么处理呢?


如下图所示,服务端进程在处理客户端请求的时候,大致需要进行3个步骤:

  • 处理连接
  • 解析与优化
  • 存储引擎


01. 处理连接

客户端向服务端发送请求并最终得到响应,其本质上是一个进程间通信的过程。通信方式有3种:

  • TCP/IP协议  (TCP/IP协议是MySQL客户端和服务端最常用的通信方式)

我们MySQL服务端默认监听端口是3306,这句话的前提是客户端进程和服务端进程使用的是TCP/IP协议进行通信,IP地址需要指定为 127.0.0.1。

  •  UNIX域套接字

MySQL服务端进程默认监听的UNIX域套接字文件为 /temp/mysql.sock。

如果客户端进程和服务端进程都位于 UNIX操作系统(MacOS、Centos、Ubuntu等)的主机之上,并且在启动客户端程序时没有指定主机名,或者指定的主机名为localhost,又或者指定了--protocol=socket的启动参数。

  •  命名管道和共享内存

使用命名管道进行通信。需要在启动服务端时添加--enable-named-pipe参数,同时在启动客户端进程时添加--pipe或者--protocol=pipe参数​​​​​​。

使用共享内存进行通信。需要在启动服务端时添加--shared-memory参数,启动成功后,共享内存便成为本地客户端程序的默认连接方式;也可以在启动客户端进程的命令中加上--protocol=memory参数明确指定使用共享内存进行通信。

通信方式确定好并且我们成功建立连接之后,MySQL有专门用于处理连接的模块——连接器。连接器就要开始验证你的身份了。信息就是 用户名 和 密码。如果用户名密码认证通过,连接器会到权限表里面查出当前登陆用户拥有的权限。之后这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。但用户名或者密码错误,客户端连接会立即断开。验权确定好了,连接已经出来完成,接下来就到下一步解析与优化了。


02. 解析与优化

服务端收到客户端传来的请求之后,还需要经过查询缓存、词法语法解析和预处理、查询优化的处理。

  • 查询缓存

如果我们两次都执行同一条查询指令,为什么第二次的响应时间会不会比第一次的响应时间短一些呢?之前使用过Redis缓存工具的读者应该会有这个很自然的想法。MySQL收到查询请求之后应该先到缓存中查看一下,看一下之前是不是执行过这条指令。如果缓存命中,则直接返回结果;否则重新进行查询,然后加入缓存。

  • 解析器&预处理器

那我们在终端里随便输入一个字符串,服务端如何判断的呢?对应的解析器就要发挥作用了,它主要包含两步,分别是词法解析和语法分析。 

分析器先会做“词法分析”,就是把一条完整的SQL语句打碎成一个个单词,比如一条简单的SQL语句,会打碎成8个符号,每个符号是什么类型,从哪里开始到哪里结束。

语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足语法,比如单引号是否闭合,关键词拼写是否正确等。

解析器会根据SQL语句生成一个数据结构,这个数据结构我们成为解析树。

词法语法分析是一个非常基础的功能,PHP的编译器、一些搜索引擎要识别语句,必须也要有词法语法分析功能。

  •  预处理器

词法解析和语法分析是无法知道数据库里有什么表,有哪些字段的。分析这些信息另外一个工具就出来了——预处理器。

它会检查生成的解析树,解决解析器无法解析的语义。比如,它会检查表和列名是否存在,检查名字和别名,保证没有歧义。预处理之后得到一个新的解析树。

  • 查询优化器&查询执行计划

一条 sql 语句可以有很多种执行方式,但最终返回是相同的结果。

例如:

select  * from students,teachers where students.id =8 and teachers.id=24

执行方式: 既可以先从表students 里面取出 id=8 的记录,再根据 id 值关联到表teachers,再判断 teachers 里面 id 的值是否=24。也可以先从表 teachers 里面取出 id=24的记录,再根据 id 值关联到表students ,再判断 students 里面 id 的值是否= 8。

这两种执行方式的逻辑结果是一样的,但是执行的效率可能会不同。多种执行方式怎么得到的呢?最终选择哪一种去执行?根据什么判断标准去选择?这个就是询优化器工作。

查询优化器的目的:根据解析树生成不同的执行计划(Execution Plan),然后选择一种最优的执行计划,MySQL 里面使用的是基于开销(cost)的优化器,哪种执行计划开销最小,就用哪种。 那么 优化器究竟做了什么呢?

例如∶当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表或者有多个索引可以使用的时候,选择哪个索引。优化器复杂的算法实现尽可能优化查询效率。主要做了下面的优化:

  • 子查询优化
  • 等价谓词重写
  • 条件化简
  • 外连接消除
  • 嵌套连接消除
  • 连接消除
  • 语义优化

虽然有优化器的存在,但希望大家在编写sql语句的时候还是要有意识地进行优化。

  • 执行计划

优化器最终会把解析树变成一个查询执行计划。查询执行计划展示了接下来执行查询的具体方式,比如多张表关联查询,先查询哪张表,在执行查询的时候有多个索引可以使用,实际上该使用哪些索引。

MySQL提供了一个查看执行计划的工具模拟优化器 ——EXPLAIN关键字 。

可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。


03. 存储引擎

经历千辛万苦,终于分析出最终的执行计划,然后就可以直接执行了吗?依然还不可以。需要到存储引擎中把数据查询出来。

  • 什么是存储引擎

应该把数据存储在什么位置,是内存还是磁盘?怎么从表里读取数据,以及如何把数据写入具体的表中,这些都是存储引擎需要做的工作。(它的前身叫做表处理器)

存储引擎是计算机抽象的典型代表,它的功能就是接受上层指令,然后对表中数据进行读和写。具体操作是对外完全屏蔽的,我们只需要对外实现同样的接口就可以了。

这样子可能更好理解:存储引擎就是数据库对数据进行读写的插件而已,根据不同存储的需求切换引擎。

  • 常见存储引擎的比较

比如MySQL支持的存储引擎都有哪些。

mysql> SHOW ENGINES;
+--------------------+---------+--------------+------+------------+
| Engine             | Support | Transactions | XA   | Savepoints |
+--------------------+---------+--------------+------+------------+
| InnoDB             | DEFAULT | YES          | YES  | YES        |
| MRG_MYISAM         | YES     | NO           | NO   | NO         |
| MEMORY             | YES     | NO           | NO   | NO         |
| BLACKHOLE          | YES     | NO           | NO   | NO         |
| MyISAM             | YES     | NO           | NO   | NO         |
| CSV                | YES     | NO           | NO   | NO         |
| ARCHIVE            | YES     | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | NO           | NO   | NO         |
| FEDERATED          | NO      | NULL         | NULL | NULL       |
+--------------------+---------+--------------+------+------------+

相信大家应该对sql执行的过程有清楚的认识了,来我们再回归一下流程图:

 希望对大家能有一点帮助,谢谢支持!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值