MySQL底层架构
连接器
验证客户端连接的用户名密码、校验权限、维持和管理连接。
客户端如果超过 wailt_timeout 没有动静,连接器会主动将它断开,此时客户端再次发送请求的话,就会收到错误:lost connection to MySQL server during query
# 查看数据库连接状态
show processlist;
# 查看当前wait_timeout的参数值
show variables like 'wait_timeout';
注意:尽量使用长连接,比如连接池
查询缓存
在MySQL5.7版本,执行过的语句就会以key-value的形式,保存在缓存中。key是查询的语句,value是查询的结果
查询缓存在很多情况下并不是一个有效的性能优化手段,原因在于它的弊端往往超过了它的好处。主要问题在于查询缓存的失效非常频繁:任何对表的更新操作都会导致该表上所有相关的查询缓存被立即清空。
鉴于这些问题,一般推荐仅在“静态表”上使用查询缓存。所谓静态表,指的是那些很少发生更新操作的表,如系统配置表或字典表等。在这些场景下,查询缓存才可能发挥出应有的作用,带来性能上的优化。为了支持这种“按需使用”的策略,MySQL提供了query_cache_type
参数,可以将其设置为DEMAND
。
当你确定某些查询可以受益于查询缓存时,可以通过在查询语句中显式添加SQL_CACHE关键字来启用查询缓存,例如:
SELECT SQL_CACHE * FROM test WHERE ID=5;
此外,了解当前MySQL实例的查询缓存设置也很重要。可以通过执行以下SQL语句来检查query_cache_type的当前设置:
SHOW GLOBAL VARIABLES LIKE '%query_cache_type%';
然而,值得注意的是,在MySQL 8.0及以后的版本中,查询缓存功能已经被完全移除。
分析器
当一个SQL语句提交给MySQL服务器时,如果未命中查询缓存(或在MySQL 8.0及以上版本,由于查询缓存功能已被移除,所有查询都会直接进入执行阶段),MySQL就会开始执行语句的准备工作,这包括解析和校验SQL语句。解析过程分为两个主要步骤:词法分析和语法分析。
词法分析
词法分析是解析过程的第一步。在这一步中,MySQL会将输入的SQL语句(一个由多个字符串和空格组成的文本行)分解成一个个可识别的标记(tokens)。每个标记都是SQL语句的一个基本构建块,如关键字、表名、列名等。
例如,对于语句SELECT * FROM T WHERE ID = 1,MySQL会识别SELECT为SQL操作的关键字,表明这是一个查询操作。它还会识别T为表名,ID为列名。这个过程就像阅读一句话并识别出其中的主语、谓语和宾语一样。
语法分析
在完成词法分析之后,下一步是语法分析。这一步骤依据MySQL的语法规则,检查词法分析的结果是否构成了一个语法上有效的SQL语句。换句话说,就是检查按照这些标记拼接起来的语句是否符合MySQL定义的SQL语法。
如果SQL语句不符合语法规则,MySQL会返回一个错误消息。比如,如果你误将FROM关键字拼写为FRO,像这样:
SELECT * FRO T WHERE ID = 1;
MySQL会检测到一个语法错误,并返回类似于以下的错误消息:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'fro T where ID = 1' at line 1
这个错误提示指出了出错的位置和可能的原因,帮助用户快速定位并修正问题。这一过程确保了只有语法正确的SQL语句才会进一步执行,避免了因为语法错误导致的执行失败或不预期的行为。
通过这两步解析过程,MySQL确保了它能理解用户的查询意图,并且语句是按照MySQL的规则构造的,这为后续的查询优化和执行奠定了基础。
优化器
当SQL语句通过了MySQL的分析器并且确认无语法错误后,下一步就是进入优化器阶段。优化器的主要职责是在可能的多个查询执行计划中选择一个最为高效的执行方案。MySQL优化器的决策基于成本估算,其中考虑了诸如数据读取成本、计算成本、IO成本等多个因素。优化器的目的是减少查询的总体执行时间和资源消耗,提高数据库的查询性能。
优化器的决策内容
- 选择使用哪个索引
对于访问表数据的查询,如果表上定义了多个索引,优化器需要决定基于查询条件使用哪个索引最为高效。选择正确的索引对于查询性能至关重要。
- 决定表的连接(JOIN)顺序
对于涉及多表关联的查询,各个表的连接顺序会影响到查询的效率。优化器会评估不同的连接顺序,选择一个预计最快完成查询的顺序。
优化器如何工作
以一个简单的JOIN查询为例:
SELECT * FROM t1 JOIN t2 USING(ID) WHERE t1.c=10 AND t2.d=20;
在这个查询中,我们需要从两个表(t1和t2)中JOIN数据,而且每个表都有过滤条件(t1.c=10和t2.d=20)。理论上,有两种执行方案:
- 方案1:先从表t1中筛选出c=10的记录,根据这些记录的ID值关联到表t2,然后在t2中进一步筛选出d=20的记录。
- 方案2:先从表t2中筛选出d=20的记录,根据这些记录的ID值关联到表t1,然后在t1中进一步筛选出c=10的记录。
这两种方案虽然逻辑结果相同,但执行效率可能会有显著差异。优化器会评估这两种方案的成本,选择成本更低(即更快、更省资源)的方案。例如,如果t1表数据量非常大,而t2表相对较小,那么方案2可能会被优化器选择,因为它可以更快地从t2中筛选出满足条件的少量记录,然后只对t1表进行有限的扫描。
优化器的重要性
优化器的决策对于查询性能至关重要,尤其是在处理大数据量和复杂查询时。一个好的执行计划可以节省大量的计算资源和时间,而一个不佳的执行计划可能导致查询执行缓慢,甚至在极端情况下导致数据库的响应停滞。因此,理解和学会如何通过索引、查询设计和其他数据库优化技术来辅助优化器的决策是提高数据库性能的关键。
完成优化器阶段后,执行计划确定下来,SQL语句就进入了执行器阶段,准备实际执行查询并返回结果。在执行器阶段,数据库会按照优化器选定的执行计划操作数据,完成数据的检索、计算和结果集的构建。
执行器
当MySQL完成了语句的分析和优化阶段后,就会进入到执行阶段。在执行阶段,MySQL会实际执行SQL语句,并返回查询结果。这个阶段包括几个关键步骤:
权限检查
在开始执行查询之前,MySQL首先会检查当前用户是否有足够的权限执行该查询。权限检查是一个重要的安全机制,确保只有被明确授权的用户能够访问和操作数据库中的数据。如果用户没有对应的查询权限,MySQL会返回一个错误,例如:
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T';
这个错误表明用户’b’@'localhost’没有权限执行对表’T’的SELECT查询。
值得注意的是,如果查询能够命中查询缓存(在MySQL 8.0之前的版本中,因为MySQL 8.0已经移除了查询缓存功能),权限检查会在从查询缓存返回结果时进行。此外,在SQL语句的语法分析过程中,MySQL的解析器会进行一些初步的权限预检查(precheck),例如验证用户是否有权访问指定的数据库和表。但由于某些SQL操作(如触发器)可能在运行时引用其他表,预检查无法覆盖这些运行时的权限校验,因此在执行阶段进行详细的权限检查是必要的。
执行查询
一旦权限检查通过,执行器就会开始实际执行查询。执行器与存储引擎紧密合作,遵循优化器确定的执行计划,逐行访问表数据并评估查询条件。对于给定的查询,如:
SELECT * FROM T WHERE ID=10;
执行器的操作步骤大致如下:
- 打开表:
根据查询涉及的表,执行器会打开表并准备读取数据。打开表的具体操作依赖于表的存储引擎。
- 读取数据:
执行器调用存储引擎提供的接口读取表的数据。它首先取得表的第一行,判断该行是否满足查询条件。如果满足,则将该行添加到结果集中;如果不满足,则跳过该行。
- 遍历表数据:
执行器继续调用存储引擎接口,取得表的下一行,并重复上述判断逻辑。这个过程一直持续到表的最后一行。
- 返回结果集:
执行器将遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
这样,通过执行器的操作,SQL语句得以实际执行,并生成最终的查询结果。这个过程展示了MySQL如何从接收到一个SQL语句到最终执行并返回结果的完整流程,包括权限验证、与存储引擎的交互,以及结果集的生成和返回。