引言
本文为社区首发签约文章,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
,一般咱们都会用它作为出产环境中的衔接池:
现在
Druid
现已被阿里贡献给Apache
软件基金会保护了~
OK~,回到前面抛出的问题,有了MySQL
衔接池为何还需求在客户端保护一个衔接池?
关于这个问题,信任咱们在心里多少都有点答案了,原因很简略,两者都是使用池化技能去到达复用资源、节约开支、提高功能的意图,只不过针对的方向不同。
MySQL
的衔接池首要是为了实现复用线程的意图,因为每个数据库衔接在MySQL
中都会运用一条线程保护,而每次为客户端分配衔接方针时,都需求阅历创立线程、分配栈空间….这些深重的作业,这个进程需求时刻,一起资源开支也不小,所以MySQL
使用池化技能处理了这些问题。
而客户端的衔接池,首要是为了实现复用数据库衔接的意图,因为每次SQL
操作都需求通过TCP
三次握手/四次挥手的进程,进程相同耗时且占用资源,因而也使用池化技能处理了这个问题。
其实也能够这样理解,
MySQL
衔接池保护的是作业线程,客户端衔接池则保护的是网络衔接。
2.2、SQL履行前会产生的作业
回归本文主题,当完好的SQL
生成后,会先去衔接池中测验获取一个衔接方针,那接下来会产生什么作业呢?如下图:
当测验从衔接池中获取衔接时,假如此刻衔接池中有闲暇衔接,能够直接拿到复用,但假如没有,则要先判别一下当时池中的衔接数是否已到达最大衔接数,假如衔接数现已满了,当时线程则需求等候其他线程释放衔接方针,没满则能够直接再创立一个新的数据库衔接运用。
假定此刻衔接池中没有闲暇衔接,需求再次创立一个新衔接,那么就会先建议网络恳求树立衔接。
首要会通过《TCP的三次握手进程》,关于这块就不再细聊了,究竟之前聊过很屡次了。当网络衔接树立成功后,也就等价于在MySQL
中创立了一个客户端会话,然后会产生下图一系列作业:
- ①首要会验证客户端的用户名和暗码是否正确:
- 假如用户名不存在或暗码过错,则抛出
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
履行的完好流程图,后续再逐步剖析每个进程:
- ①先将
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
,但除开修正操作外,新增、删除等操作也属于写操作,写操作的意思是指会对表中的数据进行更改。相同先上一个完好的流程图:
从上图来看,相较于查询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-log
是InnoDB
引擎专属的,而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
的作业线程,本身也保护着一个数据库衔接,这个数据库衔接实践上也维持着客户端的网络衔接,如下:
当成果集处理好了之后,直接通过Host
中记载的地址,将成果集封装成TCP
数据报,然后回来即可。
数据回来给客户端之后,除非客户端主动输入
exit
等退出衔接的命令,不然衔接不会立马断开。
假如要断开客户端衔接时,又会通过TCP
四次挥手的进程。
不过就算与客户端断开了衔接,
MySQL
中创立的线程并不会毁掉,而是会放入到MySQL
的衔接池中,等候其他客户端复用当时衔接。一般状况下,一条线程在八小时内未被复用,才会触发MySQL
的毁掉作业。
五、SQL履行篇总结
看到这儿,SQL
履行原理篇也走进了结尾,其实SQL
句子的履行进程,实践上也便是MySQL
的架构中一层层对其进行处理,理解了MySQL
架构篇的内容后,信任看SQL
履行篇也不会太难,通过这篇文章的学习后,信任咱们对数据库的原理常识也能够进一步掌握,那咱们下篇再会~