SQL的执行流程
对于一条SQL里的select,from,join等语句他们的执行顺序是怎样的?
对于一条SQL里的select,from,join等语句他们的执行顺序是怎样的?
首先最开始毋庸置疑是确认操作的数据源是什么
- 首先就是from确认数据源
- on过滤出from和join的表中符合联表规则的数据
- join执行联表操作
这一步就初步得到了操作的具体数据源,后续的所有操作都不会超过这里的数据源的范围
其次就是条件/分组过滤和聚合函数
- 首先是where语句,过滤出符合条件行数据
- group by,对where过滤好的数据进行分组
- 聚合函数,此时会执行包括select,having,或者是order by里的聚合函数(需要注意的是order by只有在有group by或者是使用窗口函数的前提下才有可能允许使用聚合函数)
- 执行having,对group by的数据进行分组过滤
最后就是筛选类的语句
- 首先是select,选择要返回的列(别名也在这时生效,因此在select之前执行的都不能使用别名,group by是个例外)
- distinct,对select选择返回的列进行去重
- 然后执行order by,对列进行排序
- 最后是limit,限制查询返回的行数。
子查询的执行顺序是怎样的?
这需要看子查询的位置,如果子查询的位置是在from,那么就是在执行from的时候,会执行子查询,生成一张临时表,然后外层查询在这个临时表上执行。
如果子查询的位置是在where或者是having上,此时并不会生成临时表,而是没执行一次外层数据的判断就会执行一次子查询性能很差。这种子查询就很容易被优化成join,提升性能
UNION 的执行顺序是怎样的?
UNION 是把多个 SELECT 的结果合并起来。执行顺序是先分别执行每个 SELECT,然后合并结果集。UNION 会去重,要额外做排序去重操作;
UNION ALL 不去重,直接拼接,性能更好。如果想对 UNION 的最终结果排序,ORDER BY 要写在最后一个 SELECT 后面,或者把整个 UNION 包成子查询再排序。
where和having都是过滤数据,看着很相似,有什么区别吗?
首先where是对from和join之后的数据源进行行数据级别的过滤,会过滤掉不满足条件的行数据。
而having他是对group by分组后的数据进行组级别的过滤,决定哪些分组符合条件,哪些分组需要过滤掉。
通常来说where过滤的数据会更多一点,因为是对from和join的表进行整体过滤。而having是对where过滤后的数据,并且group by分组后,进行组级别的过滤。过滤的数据相对少一些。
where后面不能带聚合函数,having可以。
对于一条SQL语句,在MYSQL里的执行流程是怎样的?
首先,MYSQL的架构主要分为两层:Server层和存储引擎层
Server层负责管理连接,解析SQL,生成语法树等
存储引擎层则负责具体的数据操作
具体的流程如下:
- 首先是客户端调用服务端对应的接口,服务端在执行sql前,一般由JDBC或者是ORM框架与MYSQL通过TCP建立稳定连接。此时MYSQL的连接器会检查你的账号和密码,并验证你的用户权限
- 随后,如果是MYSQL8.0之前的版本,还会去通过SQL语句查缓存。这个功能由于修改一条数据后就会导致缓存失效。因此缓存命中率很低,后来就给废除了
- 然后就是分析器,分析器会对SQL进行词法分析和语法分析。词法分析会将SQL拆分成一个个Token,识别出关键字,列,表等信息。语法分析就会根据MYSQL的语法,判断你这些token组合在一起是否正确。并生成一个抽象语法树,这个语法树就描述了这个SQL的结构
- 然后分析器会将语法树交给优化器,优化器会根据语法树决定如何使用索引,表的连接顺序,是否需要将子查询改写成join等等。并生成一份执行计划,优化器会选择代价最小的方案执行。我们使用explain查询到的就是这个执行计划。
- 然后优化器就按照执行计划开始执行,调用存储引擎的接口。
查询和更新的区别
上述讲的是查询语句的执行流程,那像update,delete,insert这类更新语句他们和select在连接,解析,优化这里的流程都差不多。主要的区别就在于执行这里
对于更新操作需要通过写日志来确保数据安全和崩溃恢复
InnoDB 会先写 redo log,记录这次修改的物理变化;然后写 undo log,记录修改前的数据用于回滚;Server 层还要写 binlog,用于主从复制和数据恢复。
为了保证 redo log 和 binlog 的一致性,MySQL 用了两阶段提交:先写 redo log 标记为 prepare 状态,再写 binlog,最后把 redo log 标记为 commit。这样即使中途崩溃,恢复时也能根据日志状态判断事务该提交还是回滚。(如果redo log是commit 并且 binlog是commit则提交;如果redo log是prepare,binlog没数据则回滚。如果redo log是prepare,binlog时commit则提交)
更新语句还涉及锁。InnoDB 默认用行锁,通过 MVCC 实现读写不阻塞。UPDATE 语句执行时会先读取要修改的行,加上排他锁,修改完成后在事务提交时释放锁。