select * from user 这条 SQL 语句,背后藏着哪些不可告人的秘密?
作为一名 Java开发人员,写 SQL 语句是常有的事,但是你知道 SQL 语句背后的处理逻辑吗?比如下面这条 SQL 语句:
select * from user where id=1
执行完这条语句后,我们就会得到 id 为 1 的用户信息。那么对于这一条 SQL 语句,MySQL服务器做了哪些处理呢?这篇文章我们就一起打卡 MySQL 数据库中对 SQL 语句的处理逻辑。
了解 MySQL 数据库的 SQL 语句内部处理逻辑有什么好处?当我们碰到 MySQL 的一些异常或者问题时,就能够直戳本质,更为快速地定位并解决问题。
想要更好的了解 SQL 语句的内部处理逻辑,我们可以先看 MySQL 的基本架构图,这样我们可以站在更高的角度去俯瞰 MySQL 数据库,MySQL 的基本架构示意图如下:
从图中,我们可以清晰的看出 MySQL 的架构和各个模块以及 SQL 语句的执行过程,MySQL 数据库整体可以分为 Server 层和存储引擎层两部分,其中 Server 层是共有的,而存储引擎层则是可以以插件的形式进行扩展。一条 SQL 语句大概会经历链接管理、解析与优化、最后到存储引擎,这三个模块。接下来我们就来聊一聊这三个模块。
连接管理
连接管理是 SQL 语句执行过程中碰到的第一关,链接管理就像一扇大门一样,控制着客户端与 Server 服务端的交互,连接管理主要工作是客户端的身份认证和连接线程的管理。
每个客户端与 Server 建立连接时,服务端都会创建一个线程来与客户端进行交互,交互的第一项内容就是验证客户端的身份,认证凭据是基于客户端发起连接请求时携带的主机信息、用户名、密码。如果认证失败,则结束连接任务,并且返回的 Access denied for user
错误。
如果认证成功,连接管理还会做一件事情,到权限表中查询出该用户的权限,在这次连接下,后续的权限判断都是基于此时读取的权限为依据,也就是说连接成功后,即使管理员对这个用户做了权限修改,也不会影响这次连接的权限验证。
连接管理需要做的事情就比较简单,主要是负责客户端与服务端进行连接,当然在连接线程上,连接管理也做了优化,并不是每个客户端执行完任务之后,就把该线程销毁,连接管理会把这些线程缓存起来,等待新的连接,这也就不会频繁的创建和销毁线程,从而节约了开销。
解析与优化
完成连接管理之后,SQL 语句执行的第二步就是解析和优化,这一步就非常的复杂,SQL 语句查询的所有操作都在这里了。我们可以将这一步细分为 4 小步。
查询缓存
在 MySQL 服务端也有缓存,这是一个非常鸡肋的功能,为什么呢?看完了你就知道了。
MySQL 服务器拿到查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。
看上去没毛病,这样做会大大提升 MySQL 的性能,然而,你想多了,MySQL 的查询缓存命中率非常的低,主要原因是如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。
还有就是缓存有可能获取到错误的数据,以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数NOW,每次调用都会产生最新的当前时间,如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的!
除了这些之外,MySQL 缓存的失效也非常的频繁,MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了 INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE 或 DROP DATABASE 语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除!
看到这里你知道查询缓存很鸡肋了吧,缓存对 MySQL 数据库来说弊大于利,所以在 MySQL 8.0 版本直接将查询缓存的整块功能删掉了
语法解析和预处理
如果查询缓存没有命中,接下来就需要进入正式的查询阶段了。因为客户端程序发送过来的请求只是一段文本而已,所以 MySQL 服务器程序首先要对这段文本做语法解析。
首先通过关键字将 SQL 语句进行解析,并且生成一个“解析树”。MySQL 解析器将使用 MySQL 语法规则验证和解析查询,例如,关键字是否使用正确、关键字的顺序是否正确或者引号是否前后匹配等。
预处理是根据一些 MySQL 规则进一步检查解析树是否合法,例如数据表和数据列是否存在,还会解析名字和别名,看看他们是否有歧义等。
查询优化
语法解析和预处理之后,你的需求就明白了,需要查询哪张表,查询的数据列是哪些、条件是什么等等。但是使用怎么样的方式是最优查询方式呢?查询优化就是来干这个事的,MySQL 的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等等。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的。
执行器
执行器会执行查询优化后的执行计划,通过与存储引擎交互,完成数据的查询操作,返回最终的数据结果。
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。
mysql> select * from user where ID=1;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
比如我们这个例子中的表 user 中,假设 ID 字段没有索引,那么执行器的执行流程是这样的:
1、调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
2、调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3、执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
到这里,执行 SQL 语句就执行完了,其实这内部还是非常复杂的。
存储引擎
到上面为止,SQL 语句就执行完了,但是与真实数据打交道的是存储引擎,存储引擎是 MySQL服务器对数据的存储和提取操作的封装模块。我们知道表是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的事情。
为了实现不同的功能,MySQL提供了各式各样的存储引擎,不同存储引擎管理的表具体的存储结构可能不同,采用的存取算法也可能不同。比如,MySQL5.7 之后默认的 InnoDB 存储引擎。
可以看出一条 SQL 语句的执行还是非常复杂的,涉及到了很多的模块,文章到这里就结束了,感谢您的阅读,希望这篇文章对你的学习和工作有所帮助,如果您觉得文章有用,欢迎点赞+转发。
最后
目前互联网上很多大佬都有 MySQL 内部架构相关文章,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。
欢迎扫码关注微信公众号:「平头哥的技术博文」,和平头哥一起学习,一起进步。
select * from user 这条 SQL 语句,背后藏着哪些不可告人的秘密?的更多相关文章
- 一条Sql语句分组排序并且限制显示的数据条数
如果我想得到这样一个结果集:分组排序,并且每组限定记录集的数量,用一条SQL语句能办到吗? 比如说,我想找出学生期末考试中,每科的前3名,并按成绩排序,只用一条SQL语句,该怎么写? 表[TScore ...
- JavaWeb 学习007-4个页面,5条sql语句(添加、查看、修改、删除)2016-12-2
需要复习的知识: 关联查询 =================================================================================班级模块学 ...
- 如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;)。
1.如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;). 2.select查询的多个字段之间要用逗号“,”分割,如果查询涉及多个表,那多个表之 ...
- 用一条SQL语句取出第 m 条到第 n 条记录的方法
原文:用一条SQL语句取出第 m 条到第 n 条记录的方法 --从Table 表中取出第 m 条到第 n 条的记录:(Not In 版本) * FROM Table id ...
- MyBatis7:MyBatis插件及示例----打印每条SQL语句及其执行时间
Plugins 摘一段来自MyBatis官方文档的文字. MyBatis允许你在某一点拦截已映射语句执行的调用.默认情况下,MyBatis允许使用插件来拦截方法调用 Executor(update.q ...
- 用一条SQL语句查出每门课都大于80分的学生的姓名
用一条SQL语句查出每门课都大于80分的学生的姓名,数据表结构如下: 建表SQL如下: ; -- ---------------------------- -- Table structure for ...
- java执行多条SQL语句
一次执行多条SQL的技术要点如下: DatabaseMetaData接口是描述有关数据库的整体综合信息,由于DatabaseMetaData是接口,所以没有构造方法,故不能使用new来创建Databa ...
- mysql(1)—— 详解一条sql语句的执行过程
SQL是一套标准,全称结构化查询语言,是用来完成和数据库之间的通信的编程语言,SQL语言是脚本语言,直接运行在数据库上.同时,SQL语句与数据在数据库上的存储方式无关,只是不同的数据库对于同一条SQL ...
- 腾讯面试:一条SQL语句执行得很慢的原因有哪些?---不看后悔系列
说实话,这个问题可以涉及到 MySQL 的很多核心知识,可以扯出一大堆,就像要考你计算机网络的知识时,问你"输入URL回车之后,究竟发生了什么"一样,看看你能说出多少了. 之前腾讯 ...
随机推荐
- nodeJs学习-04 POST数据请求,分段发送,分段接收
const http = require("http"); const querystring= require('querystring'); http.createServer ...
- Flask学习之十二 使用boostrap
英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xii-facelift 中文翻译地址:http://w ...
- ArcGIS中的连接和关联表
大多数数据库的设计指导方针都倾向于将数据库组织成多个表 - 每个表关注一个特定的主题 - 而非一个包含所有必要字段的大型表.设置多个表可以避免数据库中的信息发生重复,因为只会将信息在一个表中存储一次. ...
- 解决ViewState过于庞大的问题
这里是我将ViewState持久化保持在服务器端的代码,这样ViewState不占用网络带宽,因此其存取只是服务器的磁盘读取时间.并且它很 小,可以说是磁盘随便转一圈就能同时读取好多ViewState ...
- HZOJ 旋转子段
作者的正解: 算法一:对于30%的数据: 直接枚举区间直接模拟,时间复杂度O(N3). 算法二:对于60%的数据:枚举旋转中心点,然后再枚举旋转的端点, 我们可以用O(n)的预处理求前缀和记录固定点, ...
- Xib设计UITableViewCell然后动态加载
转自: http://www.2cto.com/kf/201202/120764.html (注:环境Mac OS X Lion 10.7.3 + Xcode 4.2.1 + iOS SDK 5.0. ...
- activiti工作流-概述
1 应用场景 1.1 业务流程 采购系统完成了企业的日常采购管理,包括采购单管理.采购单审核.入库.结算等模块. 用户角色包括:员工.部门经理.总经理.财务. 基本业务流程如下: 1.2 ...
- HTML5 meta 属性整理
<!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 --> <html lang="zh-cmn-Hans"&g ...
- poj 2993
跟poj 2996反过来了,这里比较麻烦的就是处理白棋和黑棋各棋子对应的位置 还有在最后打印棋盘式|,:,.的时候会有点繁琐(- - ACMer新手 ): 直接看代码吧: #include<cs ...
- Python--day33--当面试时候问到如何解决黏包问题?的回答。