写点什么

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

作者:简单猿
  • 2022 年 6 月 01 日
  • 本文字数:3154 字

    阅读完需:约 10 分钟

听说你写sql很溜,一条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 语句是否满足语法,比如单引号是否闭合,关键词拼写是否正确等。



词法语法分析是一个非常基础的功能,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> 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 执行的过程有清楚的认识了,来我们再回归一下流程图:



04、总结

 感谢大家的阅读,希望对大家能有一点帮助,期待和大家一起学习,一起成长!

发布于: 刚刚阅读数: 7
用户头像

简单猿

关注

长期主义 2022.05.31 加入

个人品牌:简单猿。 关注领域: 编程技术类比生活理解&终生学习成长。 本着化繁为简的理念做文章,希望通过简单的文字把复杂的理念转化为大家都可以理解。

评论

发布
暂无评论
听说你写sql很溜,一条sql查询语句是如何执行的?_sql_简单猿_InfoQ写作社区