引言

本文为社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

在上篇文章中,咱们以《MySQL架构篇》拉开了MySQL数据库的的序幕,上篇文章中将MySQL分层架构中的每一层都进行了具体论述。而在本篇中,则会进一步站在一条SQL的角度,从SQL的诞生开端,到SQL履行、数据回来等全链路进行剖析。

在此之前呢,其实也写过两篇类似的姊妹篇,第一篇讲的是《一个网络恳求的神奇之旅!》,在其间化身一个网络恳求,切身感触了从浏览器宣布后,到响应数据的回来。而在更早的时分,《JVM系列》中也有一篇文章,讲的是《一个Java方针从诞生到逝世的进程》,其间聊到了Java方针是怎样诞生的,运行时会阅历什么进程?运用完毕后又是怎样回收的。

在本篇中,则会在站在数据库的视角,再次感触“一条SQL多姿多彩的进程”!你假如认真的看完了“一个恳求、一个方针、一条SQL”这三部曲后,信任你关于程序开发又会有一个全新的深刻认知。

一、一条SQL是怎样诞生的?

SQL句子都诞生于客户端,首要有两种方法产生一条SQL,一种是由开发者自己手动编写,另一种则是相关的ORM结构主动生成,一般状况下,MySQL运行进程中收到的大部分SQL都是由ORM结构生成的,比方Java中的MyBatis、Hibernate结构等。

一起,SQL生成的机遇一般都与用户的恳求有关,当用户在体系中进行了某项操作,一般都会产生一条SQL,例如咱们在浏览器上输入如下网址:

https:///user/862486453028888/posts

此刻,就会先恳求的服务器,然后由内部实现中的ORM结构,依据恳求参数生成一条SQL,类似于下述的伪SQL

select * from juejin_article where userid = 862486453028888;

这条SQL大致描绘的意思便是:依据用户恳求的「作者ID」,在数据库的文章表中,查询该作者的一切文章信息。

从上述这个事例中能够显着感触出来,用户浏览器上看到的数据一般都来自于数据库,而数据库履行的SQL则源自于用户操作,两者是相得益彰的联系,也包含任何写操作(增、删、改),本质上也会被转化一条条SQL,也举个简略的比方:

# 恳求网址(Request URL)
https://www.xxx.com/user/register
# 恳求参数(Request Param)
{
    user_name : "竹子爱熊猫",
    user_pwd : "123456",
    user_sex : "男",
    user_phone : "18888888888",
    ......
}
# 这儿关于用户暗码的处理不行谨慎,没有做加密操作不要在意~

比方上述这个用户注册的事例,当用户在网页上点击「注册」按钮时,会向方针网站的服务器发送一个post恳求,紧接着相同会依据恳求参数,生成一条SQL,如下:

insert into table_user (user_name,user_pwd,user_sex,user_phone,....)
    VALUES ("竹子爱熊猫", "123456", "男", "18888888888", ....);

也便是说,一条SQL的诞生都源自于一个用户恳求,在开发程序时,SQL的大体逻辑咱们都会由业务层的编码决议,具体的SQL句子则是依据用户的恳求参数,以及提前定制好的“SQL骨架”拼揍而成。当然,在Java程序或其他言语编写的程序中,只能生成SQL,而SQL真实的履行作业是需求交给数据库去完结的。

二、一条SQL履行前会阅历的进程

通过上一步之后,一条完好的SQL就诞生了,为了SQL能够正常履行,首要会先去获取一个数据库衔接方针,上篇关于MySQL的架构篇曾聊到过,MySQL衔接层中会保护着一个名为「衔接池」的玩意儿,但信任咱们也都接触过「数据库衔接池」这个东西,比方Java中的C3P0、Druid、DBCP....等各类衔接池。

那此刻在这儿能够考虑一个问题,为什么数据库自己保护了衔接池的状况下,在MySQL客户端中还需求再次保护一个数据库衔接池呢?接下来一起聊一聊。

2.1、数据库衔接池的必要性

众所周知,当要在Java中创立一个数据库衔接时,首要会去读取装备文件中的衔接地址、账号暗码等信息,然后依据装备的地址信息,建议网络恳求获取数据库衔接方针。在这个进程中,因为涉及到了网络恳求,那此刻必然会先阅历TCP三次握手的进程,一起获取到衔接方针完结SQL操作后,又要释放这个数据库衔接,此刻又需求阅历TCP四次挥手进程。

从上面的描绘中能够显着感知出,在Java中创立、封闭数据库衔接的进程,进程开支其实比较大,而在程序上线后,又需求频频进行数据库操作。因而假如每次操作数据库时,都获取新的衔接方针,那整个Java程序至少会有四分之一的时刻内在做TCP三次握手/四次挥手作业,这对整个体系形成的后果可想而知….

也正是因为上述原因,因而大名鼎鼎的「数据库衔接池」上台了,「数据库衔接池」和「线程池」的思维相同,会将数据库衔接这种较为珍贵的资源,使用池化技能对这种资源进行保护。也就代表着之后需求进行数据库操作时,不需求自己去树立衔接了,而是直接从「数据库衔接池」中获取,用完之后再归还给衔接池,以此到达复用的效果。

当然,衔接池中保护的衔接方针也不会一直都在,当长期未进行SQL操作时,衔接池也会毁掉这些衔接方针,而后当需求时再次创立,不过何时创立、何时毁掉、衔接数束缚等等这些作业,都交给了衔接池去完结,无需开发者本身再去重视。

在Java中,现在最常用的数据库衔接池便是阿里Druid,一般咱们都会用它作为出产环境中的衔接池:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

现在Druid现已被阿里贡献给Apache软件基金会保护了~

OK~,回到前面抛出的问题,有了MySQL衔接池为何还需求在客户端保护一个衔接池?

关于这个问题,信任咱们在心里多少都有点答案了,原因很简略,两者都是使用池化技能去到达复用资源、节约开支、提高功能的意图,只不过针对的方向不同。

MySQL的衔接池首要是为了实现复用线程的意图,因为每个数据库衔接在MySQL中都会运用一条线程保护,而每次为客户端分配衔接方针时,都需求阅历创立线程、分配栈空间….这些深重的作业,这个进程需求时刻,一起资源开支也不小,所以MySQL使用池化技能处理了这些问题。

而客户端的衔接池,首要是为了实现复用数据库衔接的意图,因为每次SQL操作都需求通过TCP三次握手/四次挥手的进程,进程相同耗时且占用资源,因而也使用池化技能处理了这个问题。

其实也能够这样理解,MySQL衔接池保护的是作业线程,客户端衔接池则保护的是网络衔接。

2.2、SQL履行前会产生的作业

回归本文主题,当完好的SQL生成后,会先去衔接池中测验获取一个衔接方针,那接下来会产生什么作业呢?如下图:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

当测验从衔接池中获取衔接时,假如此刻衔接池中有闲暇衔接,能够直接拿到复用,但假如没有,则要先判别一下当时池中的衔接数是否已到达最大衔接数,假如衔接数现已满了,当时线程则需求等候其他线程释放衔接方针,没满则能够直接再创立一个新的数据库衔接运用。

假定此刻衔接池中没有闲暇衔接,需求再次创立一个新衔接,那么就会先建议网络恳求树立衔接。

首要会通过《TCP的三次握手进程》,关于这块就不再细聊了,究竟之前聊过很屡次了。当网络衔接树立成功后,也就等价于在MySQL中创立了一个客户端会话,然后会产生下图一系列作业:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

  • ①首要会验证客户端的用户名和暗码是否正确:
    • 假如用户名不存在或暗码过错,则抛出1045的过错码及过错信息。
    • 假如用户名和暗码验证通过,则进入第②步。
  • ②判别MySQL衔接池中是否存在闲暇线程:
    • 存在:直接从衔接池中分配一条闲暇线程保护当时客户端的衔接。
    • 不存在:创立一条新的作业线程(映射内核线程、分配栈空间….)。
  • ③作业线程会先查询MySQL本身的用户权限表,获取当时登录用户的权限信息并授权。

到这儿为止,履行SQL前的准备作业就完结了,现已打通了履行SQL的通道,下一步则是准备履行SQL句子,作业线程会等候客户端将SQL传递过来。

三、一条SQL句子在数据库中是怎样履行的?

通过衔接层的一系列作业后,接着客户端会将要履行的SQL句子通过衔接发送过来,然后会进行MySQL服务层进行处理,不过依据用户的操作不同,MySQL履行SQL句子时也会存在少许差异,这儿是指读操作和写操作,两者SQL的履行进程并不相同,下面先来看看select句子的履行进程。

3.1、一条查询SQL的履行进程

在剖析查询SQL的履行流程之前,咱们先模拟一个事例,以便于后续剖析:

-- SQL句子
SELECT user_id FROM `zz_user` WHERE user_sex = "男" AND user_name = "竹子④号";
-- 表数据
+---------+--------------+----------+-------------+
| user_id | user_name    | user_sex | user_phone  |
+---------+--------------+----------+-------------+
|       1 | 竹子①号      || 18888888888 |
|       2 | 竹子②号      || 13588888888 |
|       3 | 竹子③号      || 15688888888 |
|       4 | 熊猫①号      || 13488888888 |
|       5 | 熊猫②号      || 18588888888 |
|       6 | 竹子④号      || 17777777777 |
|       7 | 熊猫③号      || 16666666666 |
+---------+--------------+----------+-------------+

先上个SQL履行的完好流程图,后续再逐步剖析每个进程:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

  • ①先将SQL发送给SQL接口,SQL接口会对SQL句子进行哈希处理。
  • SQL接口在缓存中依据哈希值检索数据,假如缓存中有则直接回来数据。
  • ③缓存中未命中时会将SQL交给解析器,解析器会判别SQL句子是否正确:
    • 过错:抛出1064过错码及相关的语法过错信息。
    • 正确:将SQL句子交给优化器处理,进入第④步。
  • ④优化器依据SQL拟定出不同的履行计划,并择选出最优的履行计划。
  • ⑤作业线程依据履行计划,调用存储引擎所提供的API获取数据。
  • ⑥存储引擎依据API调用方的操作,去磁盘中检索数据(索引、表数据….)。
  • ⑦产生磁盘IO后,关于磁盘中符合要求的数据逐条回来给SQL接口。
  • SQL接口会对一切的成果集进行处理(除掉列、合并数据….)并回来。

上述是一个简略的流程概述,一般状况下查询SQL的履行都会通过这些进程,下面再将每一步拆开具体聊一聊。

SQL接口会干的作业

当客户端将SQL发送过来之后,SQL紧接着会交给SQL接口处理,首要会对SQL做哈希处理,也便是依据SQL句子计算出一个哈希值,然后去「查询缓存」中比对,假如缓存中存在相同的哈希值,则代表着之前缓存过相同SQL句子的成果,那此刻则直接从缓存中获取成果并响应给客户端。

在这儿,假如没有从缓存中查询到数据,紧接着会将SQL句子交给解析器去处理。

SQL接口除开对SQL进行上述的处理外,后续还会担任处理成果集(稍后剖析)。

解析器中会干的作业

解析器收到SQL后,会开端检测SQL是否正确,也便是做词法剖析、语义剖析等作业,在这一步,解析器会依据SQL言语的语法规矩,判别客户端传递的SQL句子是否合规,假如不合规就会回来1064过错码及过错信息:

ERROR 1064 (42000): You have an error in your SQL syntax; check....

但假如SQL句子没有问题,此刻就会对SQL句子进行要害字剖析,也便是依据SQL中的SELECT、UPDATE、DELETE等要害字,先判别SQL句子的操作类型,是读操作仍是写操作,然后再依据FROM要害字来确认本次SQL句子要操作的是哪张表,也会依据WHERE要害字后面的内容,确认本次SQL的一些成果筛选条件…..。

总归,通过要害字剖析后,一条SQL句子要干的具体作业就会被解析出来。

解析了SQL句子中的要害字之后,解析器会依据剖析出的要害字信息,生成对应的语法树,然后交给优化器处理。

在这一步也就相当于Java中的.java源代码变为.class字节码的进程,意图便是将SQL句子翻译成数据库能够看懂的指令。

优化器中会干的作业

通过解析器的作业后会得到一个SQL语法树,也便是知道了客户端的SQL大体要干什么作业了,接着优化器会关于这条SQL,给出一个最优的履行计划,也便是告知作业线程怎样履行功率最高、最节约资源以及时刻。

优化器最开端会依据语法树拟定出多个履行计划,然后从多个履行计划中挑选出一个最好的计划,交给作业线程去履行,但这儿究竟是怎样挑选最优履行计划的,信任咱们也比较好奇,那此刻咱们结合前面给出的事例剖析一下。

SELECT user_id FROM `zz_user` WHERE user_sex = "男" AND user_name = "竹子④号";

先来看看,关于这条SQL而言,总共有几种履行计划呢?答案是两种。

  • ①先从表中将一切user_sex="男"的数据查出来,再从成果中获取user_name="竹子④号"的数据。
  • ②先从表中寻觅user_name="竹子④号"的数据,再从成果中获得user_sex="男"的数据。

再结合前面给出的表数据,暂时剖析一下上述两种履行计划哪个更好呢?

+---------+--------------+----------+-------------+
| user_id | user_name    | user_sex | user_phone  |
+---------+--------------+----------+-------------+
|       1 | 竹子①号      || 18888888888 |
|       2 | 竹子②号      || 13588888888 |
|       3 | 竹子③号      || 15688888888 |
|       4 | 熊猫①号      || 13488888888 |
|       5 | 熊猫②号      || 18588888888 |
|       6 | 竹子④号      || 17777777777 |
|       7 | 熊猫③号      || 16666666666 |
+---------+--------------+----------+-------------+

假如依照第①种计划履行,此刻会先得到四条user_sex="男"的数据,然后再从四条数据中查找user_name="竹子④号"的数据。

假如依照第②中计划履行,此刻会直接得到一条user_name="竹子④号"的数据,然后再判别一下user_sex是否为”男”,是则直接回来,不然回来空。

相较于两种履行计划的进程,前者需求扫一次全表,然后再对成果集逐条判别。而第二种计划扫一次全表后,只需求再判别一次就能够了,很显着能够感知出:第②种履行计划是最优的,因而优化器会给出第②种履行计划。

通过上述事例的讲解后,咱们应该能够对优化器的作业进一步理解。但上述事例仅是为了帮助咱们理解,实践的SQL优化进程会更加复杂,例如多表join查询时,怎样查更适宜?单表复杂SQL查询时,有多条索引能够走,走哪条速度最快….,因而一条SQL的最优履行计划,需求结合多方面的优化战略来生成,例如MySQL优化器的一些优化原则如下:

  • ❶多条件查询时,重排条件先后次序,将功率更好的字段条件放在前面。
  • ❷当表中存在多个索引时,挑选功率最高的索引作为本次查询的方针索引。
  • ❸运用分页Limit要害字时,查询到对应的数据条数后终止扫表。
  • ❹多表join联查时,对查询表的次序重新界说,相同以功率为准。
  • ❺关于SQL中运用函数时,如count()、max()、min()...,依据状况挑选最优计划。
    • max()函数:走B+树最右侧的节点查询(大的在右,小的在左)。
    • min()函数:走B+树最左边的节点查询。
    • count()函数:假如是MyISAM引擎,直接获取引擎计算的总行数。
    • ......
  • ❻关于group by分组排序,会先查询一切数据后再一致排序,而不是一开端就排序。
  • ......

总归,依据SQL不同,优化器也会依据不同的优化原则挑选出最佳的履行计划。但需求紧记的一点是:MySQL尽管有优化器,但关于功率影响最大的仍是SQL本身,因而编写出一条优异的SQL,才是提高功率的最大要素

存储引擎中会干的作业

通过优化器后,会得到一个最优的履行计划,紧接着作业线程会依据最优计划,去依次调用存储引擎提供的API,在上篇文章中提到过,存储引擎首要便是担任在磁盘读写数据的,不同的存储引擎,存储在本地磁盘中的数据结构也并不相同,但这些底层实现并不需求MySQL的上层服务关怀,因为上层服务只需求担任调用对应的API即可,存储引擎的API功能都是相同的。

作业线程依据履行计划调用存储引擎的API查询指定的表,终究也便是会产生磁盘IO,从磁盘中检索数据,当然,检索的数据有可能是磁盘中的索引文件,也有可能是磁盘中的表数据文件,这点要依据履行计划来决议,咱们只需求记住,通过这一步之后总能够得到履行成果即可。

但有个小细节,还记得最开端创立数据库衔接时,对登录用户的授权进程嘛?当作业线程去测验查询某张表时,会首要判别一下线程本身保护的客户端衔接,其登录的用户是否具备这张表的操作权限,假如不具备则会直接回来权限不足的过错信息。

不过存储引擎从磁盘中检索出方针数据后,并不会将一切数据悉数得到后再回来,而是会逐条回来给SQL接口,然后会由SQL接口完结终究的数据聚合作业,关于这点稍后会具体剖析。

下来再来看看写入SQL的履行进程,因为读取和写入操作之间,也会存在少许差异。

3.2、一条写入SQL的履行进程

假定此刻要履行下述这一条写入类型的SQL句子(仍是依据之前的表数据):

UPDATE `zz_user` SET user_sex = "女" WHERE user_id = 6;

上面这条SQL是一条典型的修正SQL,但除开修正操作外,新增、删除等操作也属于写操作,写操作的意思是指会对表中的数据进行更改。相同先上一个完好的流程图:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

从上图来看,相较于查询SQL,写操作的SQL履行流程显着会更复杂一些,这儿也先简略总结一下每一步流程,然后再具体剖析一下其间一些与查询SQL中不同的进程:

  • ①先将SQL发送给SQL接口,SQL接口会对SQL句子进行哈希处理。
  • ②在缓存中依据哈希值检索数据,假如缓存中有则将对应表的一切缓存悉数删除。
  • ③通过缓存后会将SQL交给解析器,解析器会判别SQL句子是否正确:
    • 过错:抛出1064过错码及相关的语法过错信息。
    • 正确:将SQL句子交给优化器处理,进入第④步。
  • ④优化器依据SQL拟定出不同的履行计划,并择选出最优的履行计划。
  • ⑤在履行开端之前,先记载一下undo-log日志和redo-log(prepare状况)日志。
  • ⑥在缓冲区中查找是否存在当时要操作的行记载或表数据(内存中):
    • 存在:
      • ⑦直接对缓冲区中的数据进行写操作。
      • ⑧然后使用Checkpoint机制刷写到磁盘。
    • 不存在:
      • ⑦依据履行计划,调用存储引擎的API
      • ⑧产生磁盘IO,对磁盘中的数据做写操作。
  • ⑨写操作完结后,记载bin-log日志,一起将redo-log日志中的记载改为commit状况。
  • ⑩将SQL履行耗时及操作成功的成果回来给SQL接口,再由SQL接口回来给客户端。

整个写SQL的履行进程,前面的一些进程与查SQL履行的进程没太大差异,仅有一点不同的在于缓存那里,本来查询时是从缓存中测验获取数据。而写操作时,因为要对表数据产生更改,因而假如在缓存中发现了要操作的表存在缓存,则需求将整个表的一切缓存清空,确保缓存的强一致性。

OK~,除开上述这点区别外,另外多出了仅有性判别、一个缓冲区写入,以及几个写入日志的进程,接下来一起来聊聊这些。

仅有性判别首要是针对刺进、修正句子来说的,因为假如表中的某个字段树立了仅有束缚或仅有索引后,当刺进/修正一条数据时,就会先检测一下现在刺进/修正的值,是否与表中的仅有字段存在冲突,假如表中现已存在相同的值,则会直接抛出异常,反之会持续履行。

很简略哈~,接着再来聊聊缓冲区和日志!

其实在上篇中聊到过,因为CPU和磁盘之间的功能差距实在过大,因而MySQL中会在内存中设计一个「缓冲区」的概念,首要意图是在于补偿CPU与磁盘之间的功能差距。

缓冲区中会做的作业

在真实调用存储引擎的API操作磁盘之前,首要会在「缓冲区」中查找有没有要操作的方针数据/方针表,假如存在则直接对缓冲区中的数据进行操作,然后MySQL会在后台以一种名为Checkpoint的机制,将缓冲区中更新的数据刷回到磁盘。只有当缓冲区没有找到方针数据时,才会去真实调用存储引擎的API,然后产生磁盘IO,去对应磁盘中的表数据进行修正。

不过值得注意的一点是:尽管缓冲区中有数据时会先操作缓冲区,然后再通过Checkpoint机制刷写磁盘,但这两个进程不是接连的!也便是说,当线程对缓冲区中的数据操作完结后,会直接往下走,数据落盘的作业则会交给后台线程。
不过尽管两者之间是异步的,但关于人而言,这个进程不会有太大的感知,究竟CPU在运行的时分,都是按纳秒、微秒级作为单位。

但不论数据是在缓冲区仍是磁盘,本质上数据更改的动作都是产生在内存的,就算是修正磁盘数据,也是将数据读到内存中操作,然后再将数据写回磁盘。不过在「写SQL」履行的前后都会记载日志,这点下面具体聊聊,这也是写SQL与读SQL最大的区别。

写操作时的日志

履行「读SQL」一般都不会有状况,也便是说:MySQL履行一条select句子,几乎不会留下什么痕迹。但这儿为什么用几乎这个词呢?因为查询时也有些特殊状况会留下“痕迹”,便是慢查询SQL

慢查询SQL:查询履行进程耗时较长的SQL记载。
在履行查询SQL时,大多数的普通查询MySQL并不关怀,但慢查询SQL在外,这类SQL一般是引起响应缓慢问题的“始作俑者”,所以当一条查询SQL的履行时长超过规定的时刻束缚,就会被“记载在案”,也便是会记载到慢查询日志中。

与「查询SQL」恰恰相反,任何一条写入类型的SQL都是有状况的,也就代表着只要是会对数据库产生更改的SQL,履行时都会被记载在日志中。首要一切的写SQL在履行之前都会生成对应的吊销SQL,吊销SQL也便是相反的操作,比方现在履行的是insert句子,那这儿就生成对应的delete句子….,然后记载在undo-log吊销/回滚日志中。但除此之外,还会记载redo-log日志。

redo-log日志是InnoDB引擎专属的,首要是为了确保业务的原子性和持久性,这儿会将写SQL的业务进程记载在案,假如服务器或许MySQL宕机,重启时就能够通过redo_log日志恢复更新的数据。在「写SQL」正式履行之前,就会先记载一条prepare状况的日志,表明当时「写SQL」准备履行,然后当履行完结并且业务提交后,这条日志记载的状况才会更改为commit状况。

除开上述的redo-log、undo-log日志外,一起还会记载bin-log日志,这个日志和redo-log日志很像,都是记载对数据库产生更改的SQL,只不过redo-logInnoDB引擎专属的,而bin-log日志则是MySQL自带的日志。

不过无论是什么日志,都需求在磁盘中存储,而本身「写SQL」在磁盘中写表数据功率就较低了,此刻还需写入多种日志,功率定然会更低。关于这个问题MySQL以及存储引擎的设计者自然也想到了,所以大部分日志记载也是选用先写到缓冲区中,然后再异步刷写到磁盘中。

比方redo-log日志在内存中会有一个redo_log缓冲区中,bin-log日志也同理,当需求记载日志时,都是先写到内存中的缓冲区。

那内存中的日志数据何时会刷写到磁盘呢?关于这点则是由刷盘战略来决议的,redo-log日志的刷盘战略由innodb_flush_log_at_trx_commit参数操控,而bin-log日志的刷盘战略则能够通过sync_binlog参数操控:

  • innodb_flush_log_at_trx_commit
    • 0:距离一段时刻,然后再刷写一次日志到磁盘(功能最佳)。
    • 1:每次提交业务时,都刷写一次日志到磁盘(功能最差,最安全,默许战略)。
    • 2:有业务提交的状况下,每距离一秒时刻刷写一次日志到磁盘。
  • sync_binlog
    • 0:同上述innodb_flush_log_at_trx_commit参数的2
    • 1:同上述innodb_flush_log_at_trx_commit参数的1,每次提交业务都会刷盘,默许战略。

到这儿就大致论述了一下「写SQL」履行时,会写的一些日志记载,这些日志在后续做数据恢复、迁移、线下排查时都较为重要,因而后续也会单开一篇具体讲解。

四、一条SQL履行完结后是怎样回来的?

一条「读SQL」或「写SQL」履行完结后,因为SQL操作的特点不同,两者之间也会存在差异性,

4.1、读类型的SQL回来

前面聊到过,MySQL履行一条查询SQL时,数据是逐条回来的形式,因为假如等候一切数据悉数查出来之后再一次性回来,必然会导致撑满内存。

不过这儿的回来,并不是指回来客户端,而是指回来SQL接口,因为从磁盘中检索出方针数据时,一般还需求对这些数据进行再次处理,举个比方理解一下。

SELECT user_id FROM `zz_user` WHERE user_sex = "男" AND user_name = "竹子④号";

仍是之前那条查询SQL,这条SQL要求回来的成果字段仅有一个user_id,但在磁盘中检索数据时,会直接将这个字段独自查询出来吗?并不是的,而是会将整条行数据悉数查询出来,如下:

+---------+--------------+----------+-------------+
| user_id | user_name    | user_sex | user_phone  |
+---------+--------------+----------+-------------+
|    6    |   竹子④号    || 17777777777 |
+---------+--------------+----------+-------------+

从行记载中筛选出终究所需的成果字段,这个作业是在SQL接口中完结的,也包含多表联查时,数据的合并作业,相同也是在SQL接口完结,其他SQL亦是同理。

还有一点需求紧记:就算没有查询到数据,也会将履行状况、履行耗时这些信息回来给SQL接口,然后由SQL接口向客户端回来NULL

不过当查询到数据后,在正式向客户端回来之前,还会随手将成果集放入到缓存中。

4.2、写类型的SQL回来

SQL履行的进程会比读SQL复杂,但写SQL的成果回来却很简略,写类型的操作履行完结之后,仅会回来履行状况、受影响的行数以及履行耗时,比方:

UPDATE `zz_user` SET user_sex = "女" WHERE user_id = 6;

这条SQL履行成功后,会回来Query OK, 1 row affected (0.00 sec)这组成果,终究回来给客户端的则只有「受影响的行数」,假如写SQL履行成功,这个值一般都会大于0,反之则会等于0

4.3、履行成果是怎样回来给客户端的?

关于这个问题的答案其实很简略,因为履行当时SQL的作业线程,本身也保护着一个数据库衔接,这个数据库衔接实践上也维持着客户端的网络衔接,如下:

(二)全解MySQL:一条SQL语句从诞生至结束的多姿多彩历程!

当成果集处理好了之后,直接通过Host中记载的地址,将成果集封装成TCP数据报,然后回来即可。

数据回来给客户端之后,除非客户端主动输入exit等退出衔接的命令,不然衔接不会立马断开。

假如要断开客户端衔接时,又会通过TCP四次挥手的进程。

不过就算与客户端断开了衔接,MySQL中创立的线程并不会毁掉,而是会放入到MySQL的衔接池中,等候其他客户端复用当时衔接。一般状况下,一条线程在八小时内未被复用,才会触发MySQL的毁掉作业。

五、SQL履行篇总结

看到这儿,SQL履行原理篇也走进了结尾,其实SQL句子的履行进程,实践上也便是MySQL的架构中一层层对其进行处理,理解了MySQL架构篇的内容后,信任看SQL履行篇也不会太难,通过这篇文章的学习后,信任咱们对数据库的原理常识也能够进一步掌握,那咱们下篇再会~