一条sql是怎么执行的?

本文是在存储引擎是InnoDB的前提下

mysql中的针对表的操作可以分为增删改查四种操作,也就是常说的crud大法,根据类型分为DML(增删改)和DQL(查);今天就说下插入和查询的语句时如何mysql中执行的。

不管是DML还是DQL都是要经过连接器缓存分析器优化器执行器调用存储引擎的API。在前四个阶段都是一样的流程,具体的可以参考 mysql基础架构篇,文章中有详细简介各个模块的作用,本文就不展开说了,后面我们详细说说执行器在存储引擎上是怎么查询和修改(删除/新增)的。

小知识:

扇区: 磁盘存储的最小单位,扇区一般大小为512Byte。 

磁盘块: 文件系统与磁盘交互的的最小单位(计算机系统读写磁盘的最小单位),一个磁盘块由连续几个(2^n)扇区组成,块一般大小一般为4KB。

mysql页: mysql中和磁盘交互的最小单位称为页,默认大小是16kb,也就是4个磁盘块。也就是说mysql在进行数据读取的是默认情况下一次就是读取16kb(可以修改),即是我只查询一条大小只有1kb的数据,mysql读取的也是16kb。

一条查询语句是怎么执行的

select查询比较简单,其实就是到硬盘上按照页(16kb)把数据加载到内存,然后再去匹配where条件,找出符合条件的数据;

  1. 如果where条件没有索引,那么就是全表扫描,一次次的加载数据页到内存,然后一个个匹配。
  2. 如果where条件是主键索引,那么就会把主键索引的数据页加载到内存,然后匹配到具体的行数据返回;
  3. 如果where条件是普通索引,那么就会把普通索引的数据页加载到内存,然后匹配到符合条件的叶子结点(B+树的叶子结点),如果能够使用覆盖索引,那么就会直接返回,如果不能使用覆盖索引,则会进行回表查询(走一次主键索引查询)。覆盖索引和回表不清楚的可以参考历史文章:需要知道的索引基础知识

一条更新语句是怎么执行的

更新语句首先要按照查询的流程执行,因为肯定要先知道是哪条记录,之后才能去更新这条记录。之后就是更新了,更新操作涉及到几个日志的记录,分别是undolog(InnoDB的回滚日志) ,redolog(InnoDB的数据持久化日志),binlog(mysql server的归档日志),下面我们看下他们之间是怎么配合完成数据更新的。

  1. 首先执行引擎按照条件找数据,如果内存中存在则直接返回,不存在查询后返回。
  2. 把查询出来的这条记录先放到undolog中,用于更新失败数据回滚,具体的使用可以参考上一篇文章多并发控制mvcc
  3. 执行器调用存储引擎接口写入数据,在缓存池中修改这条记录,。
  4. 写redo日志,先把数据更新到redo日志的缓存中。
  5. 准备提交事务,把redo日志缓存刷入磁盘。这时候redolog 是prepare阶段(共有两个阶段prepare和commit),然后通知执行器完成。
  6. 执行器开始执行binlog写入磁盘。
  7. 然后调用存储引擎的事务提交接口,把redolog中的prepare改成commit状态,至此更新完成。
  8. 之后有IO线程以页为单位随机写入磁盘,把我们更新后的数据慢慢落入磁盘中。

两个日志是分开写的,所以很难保证两个日志数据一致,在恢复的时候尽量保证同时参考两个日志文件,如果一致才会提交,不一致丢弃

假如在步骤8的时候更新失败,两种情况

  • sql执行失败,这时候使用undolog中记录的历史数据进行恢复即可。
  • 断电或者进程重启,那么就会在下次重启的时候判断redolog是否是commit状态,如果是可以直接提交写入磁盘,如果不是commit状态,就会判断binlog是否完整(两阶段提交保证数据的一致性),如果完整那么数据有效,写入磁盘;如果不完整或者只有一个日志存在记录,直接丢弃即可。

为什么要用日志而不是直接写入数据库所在的磁盘呢

我们要更新的数据是存在磁盘的任意位置,是属于随机IO,效率是很低的,这样mysql的并发无法保证,而redo日志和binlog属于预写日志,都是顺序IO,直接写入即可,效率要高很多,即使失败了也可以根据日志恢复。

为什么要设计两阶段提交:

写入的新数据属于脏页,只有更新到磁盘才能是完整的数据

反证一下:

假如先写redo log 后写binlog: 如果在redolog写完,binlog还没写完,mysql崩溃重启。重启之后可以通过redolog恢复数据,但是binlog是没有这条数据的,所以后续用binlog备份数据或者进行主从同步的时候都会丢失这条数据,这样和原库的数据是不一致的。

假如先写binlog后写redolog: 在binlog写完之后mysql崩溃,由于redolog 没有写入,奔溃重启之后也不会也不会恢复数据,但是binlog中已经完整记录这条记录,所以之后binlog备份和主从同步会有这条数据,这样和原库又不一致了。

网上看的一张图,画的非常好,可以收藏一下

 

总结一下

  1. 不管是查询语句还是更新语句,都要先经过连接器查询缓存(8.0已经去掉了),分析器优化器执行器
  2. 查询语句要选按照查询条件把数据所在的整页加载到内存。
  3. 更新语句在查询的基础上利用undo log,redo log,bin log 完成数据的更新。
  4. undo log是用来sql执行失败之后回滚数据,保持事务的原子性。
  5. redo log是用于mysql崩溃恢复,保证已提交事务的ACID特性。
  6. binlog是数据记录的日志文件,用于数据备份,主从同步。
  7. 直接更新记录然后刷盘是随机IO,效率低下,所以使用redolog顺序写,提高效率。
  8. 两阶段提交能够崩溃恢复,保证数据的一致性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值