sql执行的生命周期分析

目录

第一步:连接数据库,由连接器负责跟客户端建立连接、获取权限、维持和管理连接。

第二步:查缓存

第三步:分析器分析

第四步:优化器

第五步:执行器

第六步:封装结果集

第七步:返回数据给客户端

第八步:断开连接、释放资源


第一步:连接数据库,由连接器负责跟客户端建立连接、获取权限、维持和管理连接。

 连接指令:

mysql ‐h host[数据库地址] ‐u root[用户] ‐p root[密码] ‐P 3306
连接失败
在完成经典的 TCP 握手后, 连接器就要开始认证身份, 这个时候用的就是输入的用户名和密码,
如果用户名或密码不对,你就会收到一个" Access denied for user "的错误,然后客户端程序结束执行。
连接成功
如果用户名密码认证通过,会开辟一个会话空间(session),连接器会到权限表里面查出你拥有的权限缓存到会话空间中,而连接默认是长链接,在正常情况下能够保持8个小时。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。
这就意味着,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。用户的权限表在系统表空间的mysql的user表中

查看正在连接的客户端

可以通过SHOW PROCESSLIST 指令查看当前正在连接的客户端

可以通过kill +id 杀死某一个客户点的连接,例如要杀死id为17号的连接 :kill 17

长链接潜在问题
开发当中我们大多数时候用的都是长连接,把连接放在Pool内进行管理,但是长连接有些时候会导致 MySQL 占用内存涨得特别 快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如 果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

 解决方案

1、定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。

2、如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

第二步:查缓存

当建立好连接并且做好相关的操作后就去查询缓存,如果查到结果,就不会往下进行,会直接把结果返回。缓存默认是关闭的(因为很鸡肋,mysql8之后就干掉这个功能了).

如何缓存

缓存实际是以select语句为key,以查询的结果集为value进行存储。当出现更新操作之后缓存就会变成脏数据,还是回去磁盘查询,然后重新加入缓存,更多的时候时得不偿失。

我们可以通过设置query_cache_type的值为二,设置显示指定缓存

query_cache_type有3个值 0代表关闭查询缓存OFF,1代表开启ON,2(DEMAND)代表当sql语句中有SQL_CACHE 关键词时才缓存    例如 select sql_cache * from user where id = 1  

第三步:分析器分析

如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析。
分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是 什么,代表什么。
MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符 串“ID”识别成“列 ID”。
做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句 是否满足 MySQL 语法。
如果你的语句不对,就会收到 “You have an error in your SQL syntax” 的错误提醒,比如下面这个语句 from 写成了 "fro

 当语法分析结束之后,就会进行语义分析,对sql的语义分析的结果构建一颗语法树

第四步:优化器

经过了分析器的分析之后,mysql也知道了需要做什么事情,但是在开始执行之前,还会对sql做一些优化调整。

比如说当执行这条语句的时候:select  * from  student where name=‘张三’ and age = ‘18‘。name和age都是单个索引,这时候会由优化器去选择需要走哪一个索引。

再比如说进行多表查询:SELECT *  from studentA a  JOIN studentB b where a.name = '张三' and b.`name` = '李四'  ,可以先选择去查询a.name=张三的数据后再去判断b.name是否等于李四,反过来也可以,这是要根据优化器去判断,哪样的效率会高一点,就会选择哪一个,当然两种的最终结果是一样的,只是效率上存在的问题。

第五步:执行器

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

第六步:封装结果集

执行了之后就会把匹配的数据封装在一个结果集之中

第七步:返回数据给客户端

第八步:断开连接、释放资源


 

猜你喜欢

转载自blog.csdn.net/Promise_J_Z/article/details/121779324