基础架构,一条SQL查询语句是怎么执行的

  • MySQL分Server层和存储引擎层。
  • Server层包括连接器、查询缓存、分析器、优化器、执行器,涵盖大多数核心服务功能以及所有的内置函数,所有的跨存储引擎的功能都在Server层实现,如存储过程、视图、触发器等。
  • 存储引擎层负责数据的存储和提取,采用插件机制,支持InnoDB、MyISAM、Memory等多个存储引擎。MySQL从5.5.5版本开始,默认为InnoDB引擎。

连接器

  • 连接器负责跟客户端建立连接、获取权限、维护和管理连接,命令如下:
  mysql -h$ip -P$port -u$user -p$password
  • 用户名、密码认证通过后,连接器会到权限表里读取当前用户拥有的权限,之后,这个连接里面的权限判断逻辑都依赖于此时读到的权限,因此当权限发生变更时,并不会影响已经存在的连接,只对后面新建立的连接生效。
  • show processlist命令可以查看当前建立的连接,其中Command列显示Sleep的行为空闲连接。
  • 客户端如果太长时间没动静,连接就会断开,默认为8小时,由wait_timeout参数控制。
  • 数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。但是长连接会特别耗内存,因为MySQL在执行过程中临时使用的内存是管理在连接对象中的,这些内存只有在连接断开的时候才会释放,因此长期使用长连接可能会导致OOM,表现为MySQL异常重启。这个问题的解决方案有两种,
  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  2. 如果用的是 MySQL 5.7 或更高的版本,可以在每次执行一个比较大的操作后,通过执行mysql_reset_connection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

查询缓存

  • 之前执行过的查询语句及其结果可能(为什么是可能?)会以key-value的形式缓存在内存中。key 是查询的语句,value 是查询的结果。MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。***但是大多数情况下不建议使用查询缓存,因为查询缓存往往弊大于利。***查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此除非你的业务就是一张静态表,很长时间才会更新一次,这种场景才适合使用查询缓存。
  • MySQL也提供了按需使用查询缓存的功能,可以将参数query_cache_type设置成DEMAND,这样对于默认的SQL语句都不使用查询缓存,对于需要使用查询缓存的语句,可以通过SQL_CACHE显式指定,写法如下:
select SQL_CACHE * from T where id = 10;
  • MySQL 8.0 版本直接将查询缓存的整块功能删掉了。

分析器

  • 分析器会先做词法分析,再做语法分析。
  • 词法分析器会识别关键字、表名、列名,例如通过select关键字判断是否是查询语句。
  • 根据词法分析器的结果,语法分析器会根据语法规则,判断要执行的SQL语句是否满足MySQL的语法。

优化器

  • 优化器会根据执行效率,选择要使用的索引(多索引的情况),决定表连接顺序(多表连接的情况)。

执行器

  • 执行器在执行查询之前先进行权限验证(在工程实现上(?),如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck验证权限(查询缓存的key是优化后的sql还是优化前的sql?))。
  • 慢查询日志中的*rows_examined字段表示这个语句执行过程中扫描了多少行。在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的。