当前位置: 首页 > 科技观察

SQL语句在MySQL中是如何执行的?

时间:2023-03-13 06:19:21 科技观察

前言相信大家用了这么久的MySQL,一定很好奇自己写的SQL是如何执行并返回结果的。今天我们就来一层层剥MySQL的洋葱。首先我们通过一张图来了解一下整个过程,然后开始一步步分析。第一步:客户端向MySQL服务器发送SQL语句如果项目中使用Mybatis来操作数据库,那么Mybatis会通过JDBC连接数据库,向数据库发送语句,因为运行Web后台服务的机器和MySQL服务一般都是物理分离的,是分布式架构,所以需要通过网络访问。JDBC使用TCP连接与MySQL服务器通信。通信的内容包括发送语句和接收执行结果。虽然TCP是全双工的,但是Mysql的TCP是半双工的,也就是说要么是客户端在发送数据,要么是服务端同时在发送数据。第二步:验证连接的合法性JDBC在与数据库建立连接时,会要求输入用户名和密码。mysql需要验证用户名是否存在,密码是否正确。验证通过后,根据mysql.user表中的host字段验证客户端IP是否为允许IP。这个host字段相当于一个白名单。之前的合法性通过后,JDBC将实际的SQL语句发送给MySQL服务器。第三步:查询缓存和上面的SELECT语句一样,MySQL服务器在收到SQL的时候,如果开启了查询缓存,就会根据SQL语句在查询缓存中进行查找,查找成功则直接返回查询结果缓存到客户端,不会执行后面的操作。请注意,这里的查找方式是根据SQL语句进行哈希运算,只要SQL中存在字节差异,就不会命中缓存。第四步:语法解析和预处理当查询缓存未命中时,将开始解析和预处理。语法分析就像一个编译程序,根据语句生成语法树,检查语法树中的关键字是否正确,顺序是否正确,前后引号是否匹配等等。解析完语法后,预处理会检查sql中的表和列是否存在,列名是否有歧义等。同时,预处理还会对sql进行权限认证,比如用户是否有SELECT权限,INSERT权限...,是否有相应数据库的权限,表权限等。第五步:查询优化查询优化主要分为两部分,一是静态优化,二是动态优化。静态优化可以等价交换语句中的一些where条件,例如:WHERE1=1ANDa>2将被WHEREa>2替换;静态优化不依赖于sql语句的具体值,就像Java静态编译器一样的语法糖。动态优化:由于动态优化是以页为最小单位来评估成本的,因此需要分析SQL语句对应的表的索引页数或数据页数,以确定是使用索引还是全表扫描。这些信息是通过存储引擎获得的,所以如果存储引擎给出的结果不准确,那么查询优化的执行计划可能不是最优的。因为一条SQL可以选择的执行方式有很多种,比如一张表有多个索引,SQL语句涉及到多张表的连接查询,那么在获取到以上信息后,需要评估使用哪种使用的索引和按顺序关联哪个表是最优的,并以此来生成执行计划。这部分也是Mysql服务层最复杂的部分,因为需要考虑的因素很多,笔者这里只列举了一小部分。第六步:调用存储引擎执行实际上,在MySQL中,真正决定如何存储和查询数据的组件是存储引擎。因此,在第五步得到执行计划后,MySQL会调用该表对应的存储引擎的API来执行真正的查询。Mysql定义了一系列存储引擎接口,供编写存储引擎的人去实现,所以只要存储引擎符合接口定义,就可以放到MySQL中使用。其中,使用最广泛的引擎是InnoDB。InnoDB是一个高性能的存储引擎,支持事务和从崩溃中快速恢复。Mysql服务层和存储引擎层最大的区别在于,服务层实现了一些不依赖于具体存储引擎的通用操作,比如上面的连接校验和SQL校验。存储引擎完成具体的查询和存储操作,所以一个好的存储引擎是Mysql的关键。第七步:将结果返回给客户端一种很容易想到的方式是MySQL服务器先将查询结果缓存在内存中,然后一次性发送给客户端,但事实并非如此。实际上,第一条满足条件的数据返回给客户端,这是一个增量的过程。这样做的原因是为了缓解服务器的内存压力。如果启用了查询缓存,并且语句是UPDATE、DELETE、INSERT等操作,那么此时查询缓存也会被更新。总结一下,在整个过程中,最复杂的部分是第五步的查询优化和第六步的具体存储引擎。实现细节是MySQL长期繁荣的原因。如果要优化MySQL的性能,有几个步骤可以优化:客户端使用连接池,允许连接重用,因为MySQL使用一个线程来处理每个接收到的连接,用其他的连接池来解决webservers同样的问题可以在这里解决。querycache虽然可以避免查询时很多后续操作的开销,但是维护它的成本也是相当高的,因为每次UPDATE、DELETE、INSERT都需要互斥地更新对应表的querycache,这就会成为MySQL.可扩展性瓶颈。根据阿姆达尔定律,决定一个系统能否水平扩展的是程序的串行部分。MySQL8.0以上版本,查询缓存默认是关闭的。因此,除非您可以确定查询缓存确实有助于提高吞吐量,否则禁用查询缓存是个好建议。默认情况下,在第七步的接收过程中,客户端会将所有结果缓存到自己的内存中后,才真正解除阻塞。这些将创建许多对象。当并发增加时,可能会导致JVMOOM。所以这里可以改为每次只接收一部分数据,处理后再接收一部分。但是这里服务器保存资源的状态,所以它是空间和时间之间的权衡。如果有必要,您可以在第五步中干预查询优化过程。MySQL提供了一些提示语句,例如强制关联表的顺序或强制使用某些索引。但在大多数情况下,请不要认为你比查询优化器更聪明,使用推荐的方案可能会更好。设计一个好的索引对查询性能有很大的影响,所以索引设计是使用关系数据库非常重要的一环。