MySQL服务器是如何处理客户端请求的?
当我们在学习MySQL时,通常是由客户端发起请求,向服务端查询数据并返回,但内部发生了什么其实我们并不知晓,今天我们就来剖析其内部结构。
一、MySQL执行流程是怎样的?
从图中我们可以看出,服务器在处理来自客户端的查询请求大致需要经过三个部分,分别是连接管理 、 解析与优化 、 存储引擎 ,也就是我们常说的连接层 、服务层 、引擎层,接下来我们来详细每一层结构的具体内容。
1、连接层
客户端在向 MySQL 服务器发送一条查询请求前,首先要确保MySQL服务器已经正常启动,并且客户端需要与MySQL服务器已经建立 TCP 连接。 在客户端程序发起连接请求时,要携带主机地址、用户名、密码、端口号等信息,服务器程序会对客户端程序提供的这些信息做身份认证、权限获取。
- 用户名或密码不对,会收到一个
Access denied for user
错误,客户端程序结束执行 - 用户名密码认证通过,会从权限表查出账号拥有的权限与连接关联,之后的权限判断逻辑,都将依赖于此时读到的权限。所以,如果一个用户已经建立了连接,即使管理员中途修改了该用户的权限,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。
每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出和服务器断开连接时,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。
综上,连接层的职责是负责认证、管理连接、获取权限信息等。
2、服务层
当客户端与服务器建立连接后,客户端就可以向 MySQL 服务发送 SQL 语句了,MySQL 服务器在接受 SQL 语句后,就会解析出 SQL 语句的第一个字段,看看是什么类型的语句。
mysql
#从student表中查询id=1的姓名
select last_name from student where id = 1
2.1 查询缓存
很明显上面是一条查询语句,那么首先MySQL会去查询缓存( Query Cache )里查找缓存数据,看看之前有没有执行过这一条命令,这个查询缓存是以 key-value
形式保存在内存中的,key
为 具体 SQL 查询语句,value
为 SQL 语句查询的结果。
- 如果查询的语句命中查询缓存,那么就会直接返回查询结果给客户端。
- 如果查询的语句没未命中,那么就要往下继续执行,等执行完后,将查询的结果存入查询缓存中。
这么看,查询缓存还挺有用,但是其实查询缓存挺鸡肋的。
-
如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。比如查询语句1先查询数据库,得到结果后存入查询缓存,查询语句2再次查询数据库时,并不会命中查询缓存。
mysqlselect last_name from student where id = 1 #查询语句1 select last_name from student where id= 1 #查询语句2
-
如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,那这个请求就不会被缓存。比如 NOW 函数 ,每次调用都会产生最新的当前时间, 如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的!
-
如果表的结构或者表中的数据被修改后,会导致缓存失效。如对该表使用了 INSERT 、 UPDATE 、 DELETE 、 TRUNCATE TABLE 、 ALTER TABLE 、 DROP TABLE 或 DROP DATABASE 语句,那表的所有查询缓存都将变为无效并删除!
注:
1、查询缓存并非是innodb存储引擎中的缓冲池buffer pool;
2、查询缓存是服务层的组件,也就是说可以在不同客户端之间共享。比如客户端A刚刚查询了一个语句,而客户端B在此后发送了一条一模一样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据;
3、虽然查询缓存有时可以提升系统性能,但也不得不因维护这块缓存而造成一些开销,同时查询缓存的命中率并不高,所以从MySQL 5.7.20 开始,不推荐使用查询缓存,并在MySQL 8.0中删除。
2.2 语法解析
如果查询缓存没有命中,接下来就需要进入正式的查询阶段了。因为客户端程序发送过来的请求只是一段文本,所以 MySQL 服务器程序首先要对这段文本进行解析,这部分包含词法解析
、语法解析
等,保证我们编写的SQL语言正确,从而构建出 SQL 语法树。
-
词法分析
。MySQL 会根据你输入的文本识别出关键字出来。例如下面的SQL语句,在分析完成,会得到7个token,其中有3个关键字,分别为select
、from
、where
。mysql#从student表中查询id=1的姓名 select last_name from student where id = 1
-
语法分析
。根据词法分析的结果,语法解析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法,如果没问题就会构建出 SQL 语法树。
如果我们输入的 SQL 语句语法不对,就会在解析器这个阶段报错。比如,我下面这条查询语句,把 from 写成了 form,这时 MySQL 解析器就会给报错。
2.3 查询优化
语法解析之后,服务器程序就获得了需要的信息,比如要查询的列是哪些,表是哪个,搜索条件是什么等,但光有这些是不够的,因为我们写的 MySQL 语句执行起来效率可能并不是很高, MySQL 的优化程序会对我们的语句做一些优化。比如外连接转换为内连接
、表达式简化
、子查询转为连接
等。
- 查询优化器确定 SQL 语句的执行路径,生成一个MySQL认为的最小执行成本的
执行计划
。 - 这个执行计划表明了应该使用哪些索引进行查询(全表检索还是使用索引检索),表之间的连接顺序是啥样的。我们可以使用
EXPLAIN
语句来查看某个语句的具体执行信息,这里暂时不再赘述,待到索引优化部分再提。
3、存储引擎
经历完优化器后,就确定了执行方案,接下来 MySQL 就真正开始执行语句了,这个工作是由`执行器`完成的。在执行的过程中,执行器就会和存储引擎交互,以`记录`为交互的基本单位。执行器将按照生成的执行计划调用底层存储引擎提供的API,获取到数据后返回给客户端。以`select语句`为例,服务层根据执行计划先向存储引擎层取一条记录,然后判断该记录是否符合`where`条件,如果符合,就保留并适机发给客户端,否则就跳过该记录,继续判断下一条记录,以此类推。
插件式存储引擎层( Storage Engines)
真正的负责了MySQL中数据的存储和提取。MySQL提供了多种存储引擎,不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
MySQL 8.0.25
默认支持的存储引擎如下:
其中的 Support
列表示该存储引擎是否可用,DEFAULT
值代表是当前服务器程序的默认存储引擎。 Comment
列 是对存储引擎的一个描述(英文的将就着看吧)。 Transactions
列代表该存储引擎是否支持事务处理。 XA
列代 表着该存储引擎是否支持分布式事务。 Savepoints
代表着该列是否支持部分事务回滚。
二、常见面试题
1、简述MySQL的执行流程
答:当客户端通过TCP协议与服务器连接时,MySQL服务器首先将客户端传输过来的主机地址、账号、密码等信息与mysql数据库下的user表中数据进行身份验证,若验证成功,则登录成功,若验证失败,则返回一条错误信息。
当客户端向服务器发送一次sql语句请求时,mysql服务器首先会经过解析器会sql语句进行解析,其中包含词法解析,语法解析等,解析完成后会生成一个语法树。经过解析器解析完成后,要经过优化器对语法树进行优化,最终生成一个优化器认为执行成本最低的执行计划,最终到达执行器,执行器会检查该用户的是否拥有权限等,最后调用存储引擎的API与缓冲池或磁盘交互,最终将查询返回结果给用户。
注:若MySQL版本在8.0之前,在返回结果时,还会将结果进行缓存,当下次继续发送相同语句时,MySQL会直接将sql语句作为key,从查询缓存中获取结果,不需要在经过解析器及后续操作。