共计 1982 个字符,预计需要花费 5 分钟才能阅读完成。
开发人员基本都知道,我们的数据存在数据库中(目前最多的是 MySQL 和 Oracle,由于作者更擅长 MySQL,所以这里默认数据库为 MySQL),服务器通过 sql 语句将查询数据的请求传入到 MySQL 数据库。数据库拿到 sql 语句以后。都是进行了哪些操作呢?这里向大家介绍下我的个人的理解,欢迎大家评论区批评指正。
流程概述
MySQL 得到 sql 语句后,大概流程如下:
1.sql 的解析器:负责解析和转发 sql
2. 预处理器:对解析后的 sql 树进行验证
3. 查询优化器:得到一个执行计划
4. 查询执行引擎:得到数据结果集
5. 将数据放回给调用端。
流程图如下所示:
分发器及缓存阶段
首先,如果系统的缓存功能开启着的话,sql 语句进入 MySQL 后,sql 进行判断,是否为 select 关键字。如果是,那么先去查询缓存中进行查询,如果在查询缓存中可以命中 sql 语句,那么直接返回查询缓存中的查询语句对应的 value 值(在缓存中,把查询语句做一个 hash 运算,结果作为 key 值,查询的结果集为 value)。
如果命中缓存的话,查询速度是相当快的。但是查询缓存也有它相应的缺点。
首先,开启缓存的话,服务器会消耗大量的内存空间;其次,缓存有的时候并不适用;最后,有的情况下,开启缓存也不会将对应的 sql 语句写入缓存。
缓存不适用的情况:
缓存的锁的力度比较大,而且对于动态 sql 的支持度不够。
缓存在数据进行更新的时候,是进行的表级锁,更新结束后,会把所有与更新内容相关的缓存全部删除。所以,如果表的写入比较多的话,缓存是比较浪费性能的。如果写入特别多,可能缓存反而会导致 MySQL 变慢。
查询不到缓存的情况:
1. 查询条件有不确定数据:如 now,current_time 等。
2. 缓存对大小写敏感,如 select * from test 和 SELECT* FROM test 就不会解析为同一条 sql
查询带来的额外开销:
1. 开始前需要先检查缓存是否命中。
2. 结果输出的时候,需要额外进行数据的缓存操作。
3. 写入数据时,MySQL 会将对应表的所有缓存都设置为失效。当缓存内存较大的时候,会导致系统消耗较大。
sql 的解析器与预处理
sql 解析器是在命令分发之后,将对应的 sql 语句,解析为 sql 解析树。sql 解析树是 MySQL 本身内部的语法规则和解析查询。验证是否使用错误的关键字,sql 语法顺序是否正确等。(语法层面的错误)
解析完成后,进行查询语句预处理器,根据 MySQL 的规则,检查解析树是否合法。(表格是否存在,别名是否有歧义等)
查询优化器
查询优化器获取到执行计划然后由查询执行引擎执行相应的操作。查询优化器,是数据库 l 的一个核心模块,分为 cbo 和 rbo 两种。
其中,rbo 是基于规则的优化器。(rbo 在 oracle 早期版本中使用,现在也保留,不过默认为 cbo。MySQL 没有 rbo 优化器)
这些规则是硬编码在数据库的代码中的。rbo 会根据输入的 sql 语句可以匹配到的优先级最高的规则去作为执行计划。例如:在 rbo 中有这么一条规则:有索引的情况下,使用索引。那么所有的带有索引的表在执行的时候,都会走索引。rbo 最大的问题在于,通过固定规则来决定执行计划。并不会考虑 sql 中涉及的对象的数量和分布。有可能选出来的规则不是最优的执行计划。
cbo 是基于成本的优化器(基于统计信息),从目标诸多的执行路径中选择一个成本最小的执行路径来作为执行计划。成本指的是 MySQL 根据相关的统计信息,算出来 sql 语句对应的 io,cpu 等的消耗的一个估计值。计算过程涉及到索引、表、行等数据,过程比较复杂。
1. 查询优化器使用统计信息为 sql 选择执行计划。
2.MySQL 没有数据直方图,也无法手工删除统计信息。(oracle 有)
3. 在服务器曾有查询优化器,却没有保存数据和索引统计信息。统计信息由存储引擎实现,不同的存储引擎会存储不同的统计信息。
4. 统计信息分为索引的统计信息和表的统计信息。
查看统计信息
索引统计信息
show index from table 或 information_schema.statistics 表
表统计信息
show table status like 或 information_schema.tables 表
查询执行引擎 + 返回数据给客户端
得到执行计划后,根据已有的执行计划,查询执行引擎,MySQL 的 SQL Layer 层,调用 Storage Engine Layer 层的接口,从 MySQL 的存储引擎中获取到相对应的结果集,然后返回给用户。
执行完成后,将结果返回给客户端,如果是查询语句,并且开启了缓存,那么,MySQL 会同时将结果集放到查询缓存中。然后将查到的结果集返回。如果是增删改操作,那么返回执行语句后受影响的行数。
: