zoukankan      html  css  js  c++  java
  • 1 基础架构:一条sql查询语句如何执行?

    1 基础架构:一条sql查询语句如何执行?

    分析一个最简单的查询

    mysql> select * from T where ID=10

    MySQL基本架构示意图

    大体来说,mysql可以分为server层和存储引擎层

    Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖mysql的大多数核心服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

    而存储引擎层负责数据的存储和提取,其架构模式是插件式的,支持innodbmyisammemory等多个存储引擎。现在最常用的是innodb,从mysql5.5开始成为默认的存储引擎。

    上图中,不同的存储引擎公用一个server层,也就是从连接器到执行部分。

    连接器

    第一步,先连接到db上,连接器负责跟客户端建立连接、获取权限、维持和管理连接

    mysql -h$ip -P$port -u$user -p

    连接命令中的mysql是客户端工具,用来跟服务器建立连接,在完成经典的tcp握手后,连接器就开始身份认证,输入用户名和密码

    --如果用户名或密码不正确,收到一个--access denied for user错误

    --如果通过,连接器就回到权限表查出该用户拥有的权限,之后,在这个连接里面的权限逻辑判断,都将依赖于此时读到的权限。

    一个用户成功建立连接后,即使用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限,修改完成后,只有再新建的连接才会使用新的权限设置。

    客户端如果太长时间没动静,连接器就会自动将他断开,参数wait_timeout控制,默认8小时。

    如果连接被断开之后,客户端再次发送请求,就会收到一个错误提醒:lost connection to mysql server during query。如果要继续,就需要重连,然后再执行请求。

    数据库里面,长连接是指连接成功后,如果客户端有持续请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开,下次查询再重新建立一个

    建立连接的过程通常是比较复杂的,所以建议在使用过程中尽量减少建立连接的动作,也就是尽量使用长连接。

    但是全部使用长连接后,可能会发现,有些时候mysql占用内存涨得特别快,这是因为mysql在执行过程中临时使用的内存时管理在连接对象里面,这些资源会在连接断开的时候才释放,所以如果长连接累积下来,可能导致内存占用太大,被系统强行(OOM),从现象上看就是mysql异常重启了。

    怎么解决这个问题呢?

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

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

    查询缓存

    连接建立完成后,可以执行select语句,第二步:查询缓存

    Mysql拿到一个查询请求后,会先到查询缓存看,之前是不是执行过这条语句,之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中,key是查询语句,value是查询的结果,如果你的查询能够直接在这个缓存中找到key,那么这个value就会被直接返回给客户端。

    如果语句不在查询缓存中,就会继续后面的执行阶段,执行完成后,执行结果会被存入查询缓存,可以看到,如果查询命中缓存,mysql不需要执行后面的操作,就可以直接返回,这个效率会很高。

    但是大多数情况下会建议不要使用查询缓存,因为查询缓存往往弊大于利

    查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空,因此很可能把结果存起来,还没使用就被一个更新全部清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非业务有一种静态表,很长时间才会更新一次,比如,一个系统配置表,

    Mysql提供了”按需使用”的方式,可以将参数设置为query_cache_type=demand,这样对于默认的sql语句都不使用查询缓存,而对于确定要使用查询缓存的语句,可以显示的使用sql_cache

    mysql> select SQL_CACHE * from T where ID=10

    mysql 8.0版本已经将查询缓存功能删掉。

    分析器

    如果没有命中查询缓存,就要开始真正执行语句了,分析器会先做词法分析mysql要从sql语句识别出字符串分别是什么,代表什么

    做完词法分析,就要做语法分析,根据语法规则判断sql是否合法。--生成一个对应的解析树

    如果语句不对,会收到you have an error in you sql syntax的错误提醒

    mysql> elect * from t where ID=1;
    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 'elect * from t where ID=1' at line 1

    分析每一个字符串是什么代表什么

    优化器

    在开始执行之前,还要经过优化器的处理

    优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联join的时候,决定各个表的连接顺序,

    mysql> select * from t1 join t2 using(ID)  where t1.c=10 and t2.d=20;

    --既可以先从表t1取出c=10id值,再根据id值关联表t2,在判断t2里面的d是否=20

    --页可以先从表t2里面取d=20id值,再根据id关联t1,再判断t1里面的c是否=10

    这两种执行方法的逻辑结果是一样,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。

    执行器

    开始执行的时候,要先判断一下对这个表t是否有执行查询的权限,如果没有,就会返回没有权限的错误,

    mysql> select * from T where ID=10;
    
    ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

    如果有权限,就打开表继续执行,打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口

    比如这sql中的表tid字段没有index,那么执行器的执行流程

    --1 调用innodb引擎接口取这个表的第一行,判断id值是否=10,如果不则跳过,如果是则将这些行缓存在结果集中

    --2 调用引擎接口取”下一行”,重复相同的判断逻辑,直到取到这个表的最后一行

    --3 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

    语句执行完成

    对于有index的表,执行的逻辑也差不多,第一次调用的是”取满足条件的第一行”这个接口,之后循环去”满足条件的下一行”这个接口,这些接口在引擎中已经定义好

    会在数据库的慢查询日志中看到一个rows_examined的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。

       在有些场景下,执行器调用一次,在引擎内部扫描了多行,因此引擎扫描行数跟rows_examined并不是完全相同的。

    思考题

    如果在表t中没有字段k,而sql执行了select * from t where k=1,会报错列k不存在,这个错误是在上面那个阶段报出来的呢?

    --

  • 相关阅读:
    利用 runtime,解决多次点击相同 button,导致重复跳转的问题-b
    dbcp数据库连接池管理
    dbutil组件的常见用法
    事务的概念及常用方法
    jdbc链接基础
    MySQL数据约束和关联查询
    mysql基本操作语句
    jsp常用标签和标签库及javaBean规范
    EL表达式
    会话过程保存数据对象cookie和session
  • 原文地址:https://www.cnblogs.com/yhq1314/p/11004064.html
Copyright © 2011-2022 走看看