结构介绍:查询语句是如何执行的
总体来说,mysql分为了Server层与存储引擎层
- Server层:连接器、查询缓存、分析器、优化器、执行器。
跨存储引擎的功能、mysql的核心服务功能、所有的内置函数,都在这一层完成。 例如:存储过程、视图、数学函数、加密函数 - 存储引擎层:负责数据的存储和提取。
插件式 常见:
- Innodb: mysql5.5.5之后的默认存储引擎,支持事务、行锁、外键,适用于读写场景
- MyISAM: 不支持事务、行锁、外键,保存具体的数据量,所以select count(*) from table执行速度非常快。适用于读场景
- Memory:创建内存临时表时的默认存储引擎
一条查询sql的执行流程:
-
建立连接 mysql -h$ip -P$port -u$user -p
上述命令中的"mysql"指mysql数据库的客户端连接工具,此命令输入完成后将与服务器建立TCP连接 几点注意:
-
不建议直接在-p参数后缀密码,因为这样导致密码明文直接暴露在数据报文中,不安全。 -
如果密码错误,会报“Access denied for user”的错误 -
连接成功后,连接器会自动进入权限表(mysql自带的四个数据库之一的mysql数据库下的表都是权限表)读取此连接用户的权限 -
第3条意味着,一旦用户连接到mysql服务器,管理员之后对权限进行的修改操作不能影响已连接的用户 -
如果客户端一段时间没有进行操作,则mysql会自动断开服务,这个时间由wait_timeout变量设置。 查看mysql全局变量的命令是 show global variables like 'wait_timeout'
设置mysql全局变量的命令是 set global wait_timeout=28800;
-
连接完成后,可以通过命令来查看已建立的mysql连接 show processlist
问题:
- 我们已经知道了客户端连接mysql服务器实际是建立了TCP连接,TCP连接的建立是比较耗费资源的。所以我们在项目中通常使用连接池来进行连接的复用,如果全部使用长连接,并且长时间不清理连接的话,就会造成mysql内存飞涨,使得连接被MySQL强行kill掉,造成连接异常中断。
解决方案:①定期断开长连接,或者每当进行复杂查询之后手动断开长连接,在下次需要查询时重新连接。②MySQL5.7以上支持mysql_reset_connection函数用来初始化连接(注意,这个函数是C的api,不是命令行命令) - 查询缓存
第一步查询的结果会以key-value键值对的形式存放在缓存中,key是查询的语句,value是查询的结果。如果命中缓存,则直接返回结果,后续步骤不再执行。 但是缓存存在一个致命弱点:一旦这张表上发生数据变化,这个表上全部的缓存都会失效。 所以MySQL8.0以后,整个移除了缓存模块。 - 分析器
分析器进行sql语句的词法分析、语法分析。 - 优化器
在表中有多个索引的时候,决定本条sql选用哪个索引。 在多表连接的时候,决定join连接各表的顺序。 - 执行器
通过分析器,MySQL知道了用户要做什么;通过优化器,MySQL知道了该怎么做最好。 在执行器执行sql之前,需要验证用户对操作的表是否有读的权限(实际上,在分析器知道用户要做什么之后,就会进行权限验证,这里的验证操作称为precheck验证,但是precheck验证无法对运行时涉及到的表进行权限验证,比如使用了触发器的情况,因此需要在执行器之前再次验证) 如果用户有读表的权限,执行器就会调用存储引擎提供的API接口得到数据,再验证数据是否满足条件,满足则放入结果集中。 在这个过程中,执行器总共拿到了多少行的数据,记录在慢查询日志的rows_examined字段中。注意:某些场景下,执行器获取一行数据,引擎扫描了多行,所以,引擎扫描行数不等于rows_examined的值。
本节问题
我给你留一个问题吧,如果表 T 中没有字段 k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。你觉得这个错误是在我们上面提到的哪个阶段报出来的呢?
答案:分析器阶段报错,因为分析器在词法分析时,会对表名、字段名等字符串进行逻辑替换,将表名、字段名替换为有实际含义的程序可识别二进制码,此时就会发现,字段名无法找到实际含义。
|