听说你写 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. 存储引擎
经历千辛万苦,终于分析出最终的执行计划,然后就可以直接执行了吗?依然还不可以。需要到存储引擎中把数据查询出来。
什么是存储引擎
应该把数据存储在什么位置,是内存还是磁盘?怎么从表里读取数据,以及如何把数据写入具体的表中,这些都是存储引擎需要做的工作
。(它的前身叫做表处理器)
存储引擎是计算机抽象的典型代表,它的功能就是接受上层指令,然后对表中数据进行读和写。具体操作是对外完全屏蔽的,我们只需要对外实现同样的接口就可以了。
这样子可能更好理解:存储引擎就是数据库对数据进行读写的插件而已,根据不同存储的需求切换引擎。
常见存储引擎的比较
比如。
相信大家应该对 sql 执行的过程有清楚的认识了,来我们再回归一下流程图:
04、总结
感谢大家的阅读,希望对大家能有一点帮助,期待和大家一起学习,一起成长!
版权声明: 本文为 InfoQ 作者【简单猿】的原创文章。
原文链接:【http://xie.infoq.cn/article/e7af6c531d0e6d6653830c101】。文章转载请联系作者。
评论