作为一名Java开发人员,写SQL语句是常有的事,但是SQL语句背后是什么样的处理逻辑却可能从来没有想过。
想要了解SQL语句的内部处理逻辑,我们可以先看MySQL的基本架构图,这样我们可以站在更高的角度去俯瞰MySQL数据库。
从图中,我们可以清晰的看出MySQL的架构和各个模块以及SQL语句的执行过程,MySQL数据库整体可以分为Server层和存储引擎层两部分,其中Server层是共有的,而存储引擎层则是可以以插件的形式进行扩展。一条SQL语句大概会经历连接管理、解析与优化和存储引擎三个模块。接下来我们就来聊一聊这三个模块。
连接管理
连接管理是SQL语句执行过程中碰到的第一关,就像一扇大门一样控制着客户端与Server服务端的交互,连接管理主要工作是客户端的身份认证和连接线程的管理。
当每个客户端与Server建立连接时,服务端都会创建一个线程与客户端进行交互,而交互的第一项内容就是验证客户端的身份,认证凭据是基于客户端发起连接请求时携带的主机信息、用户名、密码,如果认证失败,则结束连接任务,并且返回【Access denied for user】错误;如果认证成功,连接管理就会到权限表中查询出该用户的权限,在这次连接下的后续权限判断都是基于此时读取的权限为依据,也就是说连接成功后,即使管理员对这个用户做了权限修改,也不会影响这次连接的权限验证。
简单来说,连接管理需要做的事情是比较简单的,主要就是负责让客户端与服务端能够进行连接。当然,在连接线程上连接管理是做了优化的,比如并不是每个客户端执行完任务之后就会把该线程销毁,而是会把这些线程缓存起来(线程池)并等待新的连接,避免了过于频繁创建和销毁线程,从而节约了开销。
解析与优化
在完成了连接管理之后,SQL语句执行的第二步就是解析和优化,这一步就非常的复杂,SQL语句查询的所有操作都在这里了。我们可以将这一步细分为4小步。
1.查询缓存
MySQL服务器拿到查询请求后,会先到查询缓存看看之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果。如果你的查询能够直接在这个缓存中找到key,那么这个value就会被直接返回给客户端。如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。
但实际上这样的查询缓存是很鸡肋的,一是MySQL的查询缓存命中率非常的低,如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中;二是缓存有可能获取到错误的数据,以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数NOW,每次调用都会产生最新的当前时间,如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的;三是MySQL缓存的失效也非常的频繁,MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或DROP DATABASE语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。
MySQL的开发者们也意识到了这个机制弊大于利,因此在MySQL 8.0版本中直接将查询缓存的整块功能删掉了。
2.语法解析和预处理
如果查询缓存没有命中,接下来就需要进入正式的查询阶段了。因为客户端程序发送过来的请求只是一段文本而已,所以MySQL服务器程序首先要对这段文本做语法解析。
首先通过关键字将SQL语句进行解析,并且生成一个【解析树】,MySQL解析器将使用MySQL语法规则验证和解析查询,例如,关键字是否使用正确、关键字的顺序是否正确或者引号是否前后匹配等。
预处理则是根据一些MySQL规则进一步检查解析树是否合法,例如数据表和数据列是否存在,还会解析名字和别名,看看他们是否有歧义等。
3.查询优化
语法解析和预处理之后,你的需求就明白了,需要查询哪张表,查询的数据列是哪些、条件是什么等等。但是使用怎么样的方式是最优查询方式呢?查询优化就是来干这个事的,MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等等。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的。
4.执行器
执行器会执行查询优化后的执行计划,通过与存储引擎交互,完成数据的查询操作,返回最终的数据结果。
开始执行的时候,要先判断一下你对这个表有没有执行查询的权限,如果没有,就会返回没有权限的错误 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用precheck验证权限);如果有权限,就打开表继续执行。在打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
存储引擎
到上面为止,SQL语句就执行完了,但是与真实数据打交道的是存储引擎,存储引擎是MySQL服务器对数据的存储和提取操作的封装模块。我们知道表是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的事情。
而为了实现不同的功能,MySQL提供了各式各样的存储引擎(比如MySQL5.7之后默认的InnoDB存储引擎),不同存储引擎管理的表具体的存储结构可能不同,采用的存取算法也可能不同。
"除了你自己,没有别人能救赎你孤独的灵魂。"