MySQL 的一条语句是如何执行的?

MySQL的分层

客户端

比如说我们的java应用或者说自己下载的MySQL的客户端。然后还有其他的各种可视化连接工具。他们都可以认为是一个客户端。

服务端

在这里插入图片描述
服务端包括的东西比较多。

连接器

其中有连接器,连接器就是负责管理和我们各种客户端的连接以及连接权限。

分析器

分析器主要是做语法和词法的分析。

优化器

优化器就是生成我们的执行计划。

执行器

真正执行sql,调用引擎的接口和引擎做交互。

服务端的流程

当我们一条查询sql执行的时候,其实他在这个sql执行之前,他肯定是需要建立连接的,就比如说我们的一个java应用,其实我们一般会配置一个连接池。就是在启动的时候就会有一个连接池。

然后当执行一条sql的时候,首先就是我们的一条sql需要先查询缓存,但是这个查询缓存在8.0之后已经被废弃掉了。因为官方认为这个查询缓存的性能收益并不高。因为缓存的维护是需要有代价的。他的这个弊大于利。因为你有任何一个更新的话,这个缓存都是要失效掉的。然后如果说缓存命中的话,这一块就会直接返回,如果说缓存没有命中的话,就会到分析器。

分析器就是类似于一个编译器做编译的语法词法分析,他需要分析你的这个sql语句的作用,查询哪个表,什么条件。

分析器之后是优化器,因为在分析器之后他知道你要干什么,但是如果做以及如何做效率更快,所以需要优化器对各种执行做一个性能的评估或者代价的评估,他会挑一个代价比较小的。这一块代价比较小只是优化器他认为的代价比较小。实际上并不一定。只能说大部分情况,他是对的,但是当他出现错误的时候,我们要诱导他,或者我们强制的通过force index的方式来纠正他。然后优化器知道了怎么样做最快的时候,真正来到了我们的执行器,由他来执行。

执行器就是调用引擎提供的接口来返回数据。

存储引擎

然后引擎的话是一个插件式的。我们常见的有memory,然后myisam和innodb。

memory是一个内存型的,这个用的会比较少。然后就是myisam,myisam现在用的也不多,myisam不支持事务,他也不支持行锁。然后他这一块还有一个比较突出的是他的表的行数他都会自己做存储。所以你查myisam的表的数量的时候,他会比较快。

但是这一块innodb并没有这样做,是因为innodb要支持事务,不同事务其实对一个表可见行并不是一样多的。所以说,其实对于innodb来说你维护一个表的数量在那也没有太大的收益。因为大家查询到的结果都不一样。

然后还有就是myisam的一个索引和innodb的索引的区别:
myisam并没有innodb这样的一个主键索引,一棵树上直接存储了整行数据的,myisam全都是类似于innodb的二级索引,通过这个索引找到行的一个位置信息,然后查询数据。

而innodb的主键,他的二级索引也存的是主键的id,然后通过主键索引来查询到这个行数据。这个是这3个存储引擎的一些区别和特性。然后一条sql的话,查询的话就是这样执行的。

SQL的更新

还有就是更新,更新的sql其实和查询是一样的。因为更新之前,他一定要查找这个sql,之后才能够做修改。

那么这个查询的整个流程,他都要再走一遍。当这个查询流程走完的时候,这个server已经拿到了这个行数据了。然后他做一个修改,修改之后再由这个执行器调用引擎的接口,把数据做一个真正的修改。这就是一个更新的过程。

但是更新的时候会涉及到一些日志,因为mysql是write ahead log,就是写之前,他是先写日志,然后他这块的日志又分为两部分,一部分是redo log,一个是bin log。然后redo log就是下图中的这样的数据结构,它是一个环形的日志,它是有4G, 它有两个点位,一个是write point,一个是check point。

write point就是他写的点位。
check point就是他擦的点位。

写的时候就是这样顺时针往后写。一种比较极端的情况是写的点位追上了擦的点位。那么你就不能再写了,一定要等擦的点位往前挪动。这时你才可以继续写。这是redo log的一个结构。

bin log就是一个磁盘的全量日志,只要你的磁盘够大,它这个bin log是只管追加写的,然后他并不是这样的一个环形的。
在这里插入图片描述

MySQL实现崩溃恢复的原理

比如说我们要把这个数据1变成2,然后他这个执行过程就是它涉及到一个两阶段提交。

就是他首先会记录一个redo log,redo log在这个阶段就是就绪阶段,它并没有提交。

然后他再记录一个bin log,bin log记录完了之后再让这个redo log进入一个commit状态。

在这里插入图片描述
这个时候,他这个数据就变更完成了。但是他在任意时刻都是有可能崩溃的。比如说第一种情况,连这个redo log都还没有记录的时候,他的prepare状态都没有的时候,他就崩溃了。

那么其实这一条数据他就没有。因为他这个数据相当于是没有发生。

第二种情况就是这个后一步redo log的prepare状态已经记录了,但是bin log还没有写入。这种情况如果说你崩溃了,他恢复之后,这个数据其实也还是1的。因为他崩溃恢复之后检查这个redo log,他只有一个prepare状态,相应的bin log并没有,所以说这个数据就直接被删除了。他就还是1。

然后再往下一种就是redo log的prepare状态也记录了,bin log也记录了,但是redo log的commit状态并没有,就是在最后一步崩溃了。这个时候他崩溃恢复的时候,他会检查这个日志redo log prepare状态也有,然后bin log也记录的有,这个时候他就会把这个redo log变成一个commit状态,然后这个数据就会被写成2。这个时候崩溃的话,他就会恢复了。因为2真的被他恢复了,然后再往后一种状态,就是redo log的commit的日志也记录了,然后这个时候他再崩溃,其实跟上一种是一样的。

前两种的崩溃,他的数据是恢复不了的,因为对于他这个日志来说,你这个数据是真正的没有发生过的。只是在这种情况下,他保证了你不管是你通过什么状态来恢复,他的数据是一致的,比如说第一种情况或者前面两种情况,你这个只有这个redo log的prepare或者你连这个redo log的prepare都没有,你通过bin log的日志回放的时候,因为通常你需要恢复数据的时候或者说回滚数据的时候,你都是把这个bin log来指定到某一点。然后我来做一个数据恢复。这种情况下你只有redo log prepare状态或者你连这个状态都没有,所以这个时候,他的数据是1,然后你的bin log其实记录的他也还是1。

当你这个时候把bin log指到这个状态的时候,他们都是1是一致的。然后另一种状态就是bin log其实已经记录了把1变成2,然后这个时候你崩溃恢复,他也会把这个1变成2,然后或者说你其他的方式,你通过bin log做一个日志回放。你的bin log记录的也是2。这样的话,他的数据就能够保持一致或者说你有一些主从同步的数据,也能够通过同步他的bin log,然后恢复他的2,后面的状态其实是一样的。

参考资料MySQL 的一条语句是如何执行的?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值