咱们都常用Mysql做为数据库来存储与查询较经常使用的数据。当咱们输入一行如SELECT * FROM table_name WHERE id=26
这样的语句以后,Mysql若是正确执行的状况下,会输出你想要的信息。mysql
那么,在你输入这行语句以后,一直到它显示出你想要的信息,这中间Mysql都经历了什么呢?这篇文章会简单聊一下这个事情。sql
咱们先看下Mysql的一个较总体的架构图。数据库
接下来我会以具体的SQL语句为例,详细的叙述从你在客户端输入了这个语句以后,到它返回你想要的信息,这中间具体经历了什么。缓存
所谓客户端,便是咱们登陆与操做Mysql所使用的终端。咱们都是在客户端对Mysql进行操做的,不管是输入链接数据库的信息,仍是输入查询某个表的SQL,或者是收到Mysql返回给咱们的查询信息,这些都是在客户端完成的。markdown
咱们在一个客户端跟前,想要使用Mysql数据库,那么第一步就是要先链接上你要使用的数据库。架构
咱们都知道,咱们要输入命令mysql -h$ip -P$port -u$username -p
。优化
以后客户端会要求咱们输入密码。再以后,若是咱们输入的信息都没有问题了,咱们就进入Mysql的操做界面了。spa
若是咱们输入的信息有问题,就会收到客户端返回的报错信息。好比咱们将密码输入错误了,这时就会收到"Access denied for user"这样的报错信息。code
那么,这中间链接器具体作了什么呢?orm
首先,链接器会拿着咱们输入的IP和端口,去作最经典的TCP握手,握手若是都失败了,那就天然没有后续了,直接返回相应的报错信息。
若是握手成功了,此时则会去验证咱们输入的用户名和密码,验证失败则一样会返回相应的报错信息。
若是用户名密码也没有问题,接下来链接器则会取出权限表读取该用户相应的权限数据。用户跟着所作的全部操做,都基于此时读取到的用户权限。
权限表共有4个:user, db, tables_priv, columns_priv
。
当用户经过权限验证,进行权限分配时,按照user, db, tables_priv, columns_priv的顺序进行分配。即先检查用户的全局权限表user,若是user中对应的权限为Y,则此用户对全部数据库的权限都为Y,将再也不检查剩余3个表;若是为N,则到db表中检查此用户对应的具体数据库,并获得db中为Y的权限;若是db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y;若是为N,则到columns_priv中检查具体的列。
这也就意味着,当咱们修改了某个用户的用户权限,只有到下一次该用户登陆(建立新的链接)时,才会影响到该用户。
咱们能够经过show processlist
来查看当前全部的用户链接及其行为。
但若Command显示的状态是Sleep,那么说明该用户当前在等待状态。若等待超过了一段时间,则链接器会自动断开。
该超时时间由wait_timeout变量控制,能够经过show global variables like 'wait_timeout'
来查看。
所谓长链接,即用户的持续操做使用的都是同一个链接,链接在一段时间内长时间创建。
所谓短链接,即用户每作几回操做则断开,再下次操做时再进行链接。
长链接的优势是,在持续操做时,能够节省不少创建链接所须要消耗的时间。可是长链接所要存储的临时数据都在链接对象中,长时间积累,会致使系统内存溢出,具体表现 为Mysql异常重启。
短链接的优缺点与长链接相反,虽然不用担忧内存溢出的问题,但短链接在持续操做的状况下屡次链接,链接消耗不少时间,总体操做效率会很低。
链接器链接完成的下一步就是缓存器的缓存查询,若是咱们须要对一张静态表(不常更新)常常作查询操做,那么可能会用到缓存器。
缓存器中使用的是key-value的存储形式,key值存储的是查询语句,value值存储的是对应结果。
要注意的是,只要该表作了一次更新操做,那么该表对应的缓存就会所有被清理。所以使用场景并很少。
因此当前缓存器的使用较少。咱们能够经过query_cache_type
来查看缓存器是否开启。
假设咱们不使用缓存器,或者经过缓存器没有命中SQL语句。
那么链接器作链接操做以后,接下来咱们就输入了一个查询语句,好比:SELECT host FROM mysql.user LIMIT 1
。
而分析器作的事情就是对你输入的语句作 “词法分析” 与 “语法分析”。
所谓 “词法分析” ,就是判断每个你输入的词,好比分析器首先会判断出你输入的第一个词是“SELECT”,第二个词你输入了“host”,等等。
而 “语法分析” 则是跟在 “词法分析” 以后,就是依据你输入的这些词来判断你输入的是否符合语法规则。
假如符合语法规则,则会顺利进行下去并返回相应信息。
在分析器工做结束后,若是语法有问题,那么就会直接返回报错信息,且不继续向下运行。
若语法正确,那么,则会到优化器部分的工做。优化器顾名思义,就是对该语句的执行作优化。
好比,在一个语句查询某个表时,该表可能有多个索引,此时使用哪一个索引会使语句的执行效率最高?这就是优化器要作的事情。
再好比,执行语句select * from t1 join t2 on t1.ID=1 and t2.ID=2
,
该语句执行时,是先从t1表中找到ID=1的行关联到t2表以后,再从t2表中查找ID=2的行。
仍是先从t2表中找到ID=2的行关联到t1表以后,再从t1表中查找ID=1的行。
两种执行顺序可能就致使执行效率的不一样,怎样选择执行顺序会提升执行效率,这也是优化器要作的事情。
在上述步骤完成以后,就轮到执行器去执行具体的语句了。
例如语句:select * from mysql.tables_priv
在执行器作具体的语句执行以前,会对该表的操做权限进行验证,验证失败则返回权限错误的报错。以下:
而precheck是没法对运行时涉及到的表进行权限验证的,好比使用了触发器的状况。所以在执行器这里也要作一次执行时的权限验证。
若是验证成功,那么则会使用该表对应的存储引擎的接口,继续执行语句。 最后将成功执行的结果返回给客户端。
简单来讲,一条SQL语句在Mysql中执行,一共会经历四步(算上链接Mysql),分别是链接、分析、优化与执行。每一步都会精确执行,若是发现有问题就会返回给客户端相应的报错。只有每一步都正确执行,最终才会在客户端获得你想要查询或操做的结果。