SQL语句的内部执行流程

发布于:2022-11-15 ⋅ 阅读:(643) ⋅ 点赞:(0)

MySQL的基本架构示意图,从中你可以清楚地看到SQL语句在MySQL的各个功能模块中的执行过程。
MySql的逻辑架构图
MySQL可以分为Server层和存储引擎层两部分。
Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。

连接器

连接器负责跟客户端建立连接、获取权限、维持和管理连接。
数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。
但是全部使用长连接后,你可能会发现,有些时候MySQL占用内存涨得特别快,所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启了。
怎么解决这个问题呢?你可以考虑以下两种方案。

定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。

如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。

但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利。

MySQL 8.0版本直接将查询缓存的整块功能删掉了,也就是说8.0开始彻底没有这个功能了。

分析器

分析器先会做“词法分析”。
根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个SQL语句是否满足MySQL语法。如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒

优化器

优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。执行的效率会有不同,决定选择使用哪一个方案。

执行器

先判断一下你对这个表T有没有执行查询的权限,如果没有,就会返回没有权限的错误,如果有权限,就打开表继续执行去使用这个引擎提供的接口。

更新语句的执行流程

重要的⽇志模块:redo log

《孔⼄⼰》这篇⽂章,酒店掌柜有⼀个粉板,专⻔⽤来记录客⼈的赊账记录。如果赊账的⼈不多,那么他可以把顾客名和账⽬写在板上。但如果赊账的⼈多了,粉板总会有记不下的时候,这个时候掌柜⼀定还有⼀个专⻔记录赊账的账本。

当有⼀条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log(粉板)⾥⾯,并更新内存,这个时候更新就算完成了。InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘⾥⾯,⽽这个更新往往是在系统⽐较空闲的时
候做,这就像打烊以后掌柜做的事。

在MySQL⾥也有这个问题,如果每⼀次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程IO成本、查找成本都很⾼。
MySQL⾥经常说到的WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写⽇志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。
InnoDB的redo log是固定⼤⼩的,⽐如可以配置为⼀组4个⽂件,每个⽂件的⼤⼩是1GB,那么这块“粉板”总共就可以记录4GB的操作。从头开始写,写到末尾就⼜回到开头循环写,如下⾯这个图所示。
在这里插入图片描述
write pos是当前记录的位置,⼀边写⼀边后移,写到第3号⽂件末尾后就回到0号⽂件开头。
checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据⽂件。
有了redo log,InnoDB就可以保证即使数据库发⽣异常重启,之前提交的记录都不会丢失,这个能⼒称为crash-safe

重要的日志模块:binlog

MySQL⾃带的引擎是MyISAM,但是MyISAM没有crash-safe的能⼒,binlog⽇志只能⽤于归档。⽽InnoDB是另⼀个公司以插件形式引⼊MySQL的,既然只依靠binlog是没有crash-safe能⼒的,所以InnoDB使⽤另外⼀套⽇志系统——也就是redo log来实现crash-safe能⼒。

这两种⽇志有以下三点不同:

  1. redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使⽤。
  2. redo log是物理⽇志,记录的是“在某个数据⻚上做了什么修改”;binlog是逻辑⽇志,记录的是这个语句的原始逻辑,⽐如“给ID=2这⼀⾏的c字段加1 ”。
  3. redo log是循环写的,空间固定会⽤完;binlog是可以追加写⼊的。“追加写”是指binlog⽂件写到⼀定⼤⼩后会切换到下⼀个,并不会覆盖以前的⽇志。

在这里插入图片描述

两阶段提交

redo log和binlog都可以⽤于表示事务的提交状态,⽽两阶段提交就是让这两个状态保持逻辑上的⼀致。

小结

redo log⽤于保证crash-safe能⼒。
innodb_flush_log_at_trx_commit这个参数设置成1的时候,表示每次事务的redo log都直接持久化到磁盘。MySQL异常重启之后数据不丢失
sync_binlog这个参数设置成1的时候,表示每次事务的binlog都持久化到磁盘。MySQL异常重启之后binlog不丢失。


网站公告

今日签到

点亮在社区的每一天
去签到