zoukankan      html  css  js  c++  java
  • 【原创】小说:我是一条DQL

    SQL执行流程图如下

    本文改编自《高性能Mysql》,烟哥用小说的形式来讲这个内容。

    序章 自我介绍

    我是一条sql,就是一条长长的字符串,不要问我长什么样,因为我比较傲娇。

    额~~不是我不说啊,因为细说起来,我可以细分为DML(Update、Insert、Delete),DDL(表结构修改),DCL(权限操作),DQL(Select)操作,一个个去介绍,我怕大家嫌我烦!
    嗯,大家没什么意见,我继续往下自我介绍了~
    由于种类太多,这里我只是一条查询SQL,也就是一句DQL。
    客户端按照Mysql通信协议,把我发送到服务端。
    当我到达服务端后,我会在一个单独的线程里进行执行。服务端要先...

    万万没想到,我又被打断了~好吧,因为我在一个线程里执行,总要有办法能看到线程的执行状态吧。Mysql提供了下面的命令,给大家查看

    SHOW [FULL] PROCESSLIST
    

    出来的结果是长下面这样的

    图里Command这一列,反应的就是这个线程当前的执行状态啦。我在这个线程的执行过程中,状态是会变化很多次。
    你看图里,有一个Sleep,这是在告诉你线程正在等待客户端发送新的请求。还有一个为Query,这代表线程正在执行查询或者正在将结果发送给客户端。
    至于其他的,还有LockedSending data等等,分别代表...

    额,好吧,唠唠叨叨了一大堆,大家居然木有嫌我烦,嗯,至于其他状态的含义大家可以去Mysql官网查询哦。
    嗯,回到刚才的话题。我到达服务端后,Mysql要判断我的前6个字符是否为select
    并且,语句中不带有SQL_NO_CACHE关键字,如果符合条件,就进入查询缓存。

    第一章 我和查询缓存的那些事

    说到查询缓存,它其实是一个哈希表,它将执行过的语句及其结果会以 key-value 对的形式,被直接缓存在内存中。
    它的key是一个哈希值,是通过查询SQL(也就是我)、当前要查询的数据库、客户端协议版本等,生成的一个哈希值,而它的value自然就是查询结果啦。

    当然,如果我要绕过查询缓存,也很简单。我可以像下面这么写:

    Select SQL_NO_CACHE * from table
    

    也可以将参数query_cache_type设置成DEMAND来绕过查询缓存。

    可是,有一天查询缓存悲伤的对我说:"你将来再也看不到我了,我已经被历史淘汰了,Mysql8.0版本开始就没有我了!"
    听到这个消息后,我表面上故作坚强的对查询缓存说:"不要方,大家会想你的!"
    然而,实际上心里想的是:"嘿嘿嘿,你个坑爹的,终于不存在了!"大家不要觉得我太邪恶,毕竟查询缓存实在是太不好用了。接下来我们来说说解析器...

    万万没想到,本来想糊弄过去的。结果...好吧,回到正题,因为

    • 只要有对一个表的更新,这个表上所有的查询缓存都会被清空
    • SQL任何字符上的不同,如空格,注释,都会导致缓存不命中

    因此,我能想到用查询缓存的表,只有一种情况,那就是配置表。其他的业务表,根本是无法利用查询缓存的特性,或许Mysql团队也是觉得查询缓存的使用场景过于局限,就无情的将它剔除。

    第二章 我和分析器的爱恨情仇

    (本文将解析器和预处理器统一称为分析器)
    话说,我离开查询缓存后,进入解析器。
    解析器:"来来来,我先对你进行词法分析,告诉我你长啥样?"
    我是下面这样的

    select username from userinfo
    

    解析器:"好,好,好。我有两个阶段,我先对你进行词法分析,我将你从左到右一个字符、一个字符地输入,然后根据构词规则识别单词。你将会生成4个Token,如下所示。"

    关键字 非关键字 关键字 非关键字
    select username from userinfo

    解析器:"接下来呢,进行语法解析,判断你输入的这个 SQL 语句是否满足 MySQL 语法。然后生成下面这样一颗语法树。"

    我:"如果语法不对呢?"
    解析器:"那你会收到一个提示如下!"

    You have an error in your SQL syntax
    

    解析器:"顺利生成语法树以后,我就将你送往预处理器!"
    预处理器:"老弟,你来拉!"
    我:"嗯!"
    预处理器:"老弟,我来帮你看看你的列名对不对,数据库的这张表里是不是真的有这个列。再看看表名对不对,如果不对,你会看到下面的错误!"

    Unknown column xxx in ‘where clause’
    

    预处理器:"最后我再给你送去做权限验证,如果你没有操作这个表的权限,会报下面这个错误!"

    ERROR 1142 (42000): SELECT command denied to user 'root'@'localhost' for table 'xxx'
    

    (这个地方,大家可能有疑问,因为有些文章说是执行器做的权限验证,可以直接拉到本文底部看说明)

    最后,这颗语法树会传递给优化器。

    第三章 我和优化器的动人过往

    在告别了解析器后,我进入了优化器。
    优化器大哥:"告诉我,你长什么样啊?"
    我说道:"大哥不要捉急,我是长这样的~"(这里优化的其实应该是语法树,我只是为了便于说明,才用SQL当例子,实际上是针对语法树进行优化)

    select t1.*
    from Table1 t1
    inner join Table2 t2
    on t1.CommonID = t2.CommonID
    

    优化器大哥:"我的任务就是帮你判断一下怎么样执行更快,比如先查Table1再查Table2,还是先查Table2再查Table1呢?判断完如何执行以后,生成执行计划就好啦!"
    我很不信任的说道:“哼,你就不会判断失误么!”
    优化器大哥:"那就要对SQL进行改写啦,比如如果你带了STRAIGHT_JOIN关键字,长下面这样"

    select t1.*
    from Table1 t1
    STRAIGHT_JOIN  Table2 t2
    on t1.CommonID = t2.CommonID
    

    "那我就知道强制先找Table1再关联找Table2啦,类似的例子还有很多,我就不一一列举了!"
    (STRAIGHT_JOIN功能同join类似,但能让左边的表来驱动右边的表,能改表优化器对于联表查询的执行顺序。)

    我说道:"哇塞,如何编写一个高效的SQL,真是一门学问啊!"
    于是,优化器大哥将我变身为一个执行计划,然后交给执行器啦~

    第四章 我和执行器的悲情经历

    我:"执行器大哥,你是用来做什么的?"
    执行器:"就是根据执行计划来进行执行查询啦。我就根据你的指令,逐条调用底层存储引擎,逐步执行。"
    MySQL定义了一系列抽象存储引擎API,以支持插件式存储引擎架构。Mysql实现了一个抽象接口层,叫做 handler(sql/handler.h),其中定义了接口函数,比如:ha_open, ha_index_end, ha_create等等,存储引擎需要实现这些接口才能被系统使用。

    末章 一些感慨

    最后一个阶段,Mysql会将查询结果返回客户端。
    唯一需要说明的是,如果是SELECT类型的SQL,Mysql会将查询结果缓存起来。至于其他的SQL,就将
    该表涉及到的查询缓存清空。

    一些疑问

    这里关于权限验证究竟在哪个阶段执行,大家可能会有一些疑问。
    之前有一个大牛的文章说是权限验证是在执行阶段,去执行前验证权限,大家如果看过他的文章,可能会有疑问。我也不是乱质疑人家,毕竟我只是一个小咖。我在这里只是发表一下我自己的论点,欢迎大家拍砖。

    论点一:权限验证在执行器中判断从逻辑上说不通
    一条查询SQL经过查询缓存、分析器、优化器,执行器。如果到最后一个阶段执行器中才发现权限不足、那不是前面一系列流程白做了,Mysql应该不至于这么傻吧~

    论点二:同《高性能Mysql》一书内容不符
    该书209页有一句话如下图所示

    该书也指明权限验证是在预处理器中执行。本文中将预处理和解析器统一划分为分析器的范畴。

    论点三:同源码不符
    我翻看了Mysql5.7.25这个版本的源码,其在处理查询这段的核心代码如下
    sql_parse.cc文件中,有这么一段代码如下

    case SQLCOM_SELECT:
     {
        //省略
        res= select_precheck(thd, lex, all_tables, first_table);
        if (!res)
          res= execute_sqlcom_select(thd, all_tables);
        //省略
      }
    

    其中select_precheck是进行权限校验。而优化器和执行器是在execute_sqlcom_select这个方法中。
    当然,大家有新的见解,欢迎留言。

  • 相关阅读:
    Building Java Projects with Gradle
    Vert.x简介
    Spring及Spring Boot 国内快速开发框架
    dip vs di vs ioc
    Tools (StExBar vs Cmder)which can switch to command line window on context menu in windows OS
    SSO的定义、原理、组件及应用
    ModSecurity is an open source, cross-platform web application firewall (WAF) module.
    TDD中测试替身学习总结
    Spring事务银行转账示例
    台式机(华硕主板)前面板音频接口(耳机和麦克风)均无声的解决办法
  • 原文地址:https://www.cnblogs.com/rjzheng/p/10668894.html
Copyright © 2011-2022 走看看