MySQL查询语句过程透明化

查询SQL从输入SQL到返回结果集,都经历了什么?

  • 有一个最简单的表 T,表里只有一个字段ID, 执行以下 SQL时, MySQL内部的执行过程是怎样的?
select * from T where id = 10;

首先看一下MySQL的基本架构示意图,从中可以清楚的看到SQL语句在MySQL的各个功能模块中的执行过程;


架构示意图

大体来说 MySQL可以分为 server层和存储引擎层两个部分;

server层:包括连接器,查询缓存,分析器,优化器,执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期,时间,数学和加密函数等),所有跨存储引擎的功能都在这一层实现,如:存储过程,触发器,视图等;

存储引擎层:负责数据的存储和提取。其架构模式是插件式的,支持 innoDB,MyISAM,Memory等多个存储引擎。电子最常用的存储引擎是 InnoDB,它是从 MySQL5.5.5版本开始默认的存储引擎;

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

  • 连接器

第一步,首先会连接到这个数据库上,这时候就接触到了连接器;
连接器负责跟客户端建立连接,获取权限,维持和管理连接。
mysql -h$ip -P$port -u$user -p
输入完命令后,需要在交互规划里输入密码。

连接命令中MySQL是客户端的工具,用来和服务端建立连接的。在完成 tcp 握手后,连接器就开始认证你的身份,这时就是对你用户名和密码校验;

  • 如果用户名或者密码不对,就会报出 Access denied for user 的错误,然后客户端程序结束执行;
  • 如果用户名密码认证通过,连接器回到权限表里查出你拥有的权限,之后,这个连接里面的权限判断逻辑,都将依赖此时督导的权限。

这意味着:一个用户成功建立连接后,及时你用管理员的账号对这个用户权限做了修改,也不会影响已经存在的连接逻辑,都将依赖于此时读到的权限;修改完之后,只有再新建的连接才会使用新的权限设置;

连接完成后,如果灭有后续的动作,这个连接线程就处于空闲的状态,可以使用 show processlist 命令查看这条命令;


其中 Command 列显示为 sleep 的这一行,就表示在系统里面处于空闲状态;

  • 客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是有参数 wait_timeout 来控制的,默认是 8小时;
  • 如果连接被断开之后,客户端再次发送请求的话,就会报错 Lost connection to mysql server during query;这个时候如果需要继续的话就得重新连接,然后才可以之心请求;
  • 数据库里面,长链接 : 是指连接成功后,如果客户端持续有请求,则一直使用同一个连接;
    短连接是指每次执行完很少的几次查询就断开连接,下次查询在重新建立一个
  • 建立连接的过程通常比较复杂,建议在使用中药尽量减少建立连接的动作,尽量使用长连接;

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

这个问题怎么解决呢,可以考虑一下两种方案

  • 定期断开长连接,使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询时再连接;
  • 如果是MySQL5.7 或者更高的版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源,这个过程不需要重连做权限验证,但是会将连接恢复到刚刚创建完时的状态;
  • 查询缓存

执行完连接器,就可以执行 select 语句了。执行逻辑来到 查询缓存

MySQL拿到一个语句时,先到查询缓存中查找之前是不是执行过这个查询。之前执行过的语句会以 key value 对的形式保存在查询缓存中,key 是查询语句, value 是查询结果。如果查询可以直接在查询缓存中命中结果,则直接返回 value 结果集给客户端;

如果没有命中查询缓存中的 key ,MySQL则继续进行查询,然后将查询结果缓存一份到查询缓存中。可以看到如果 查询时命中查询缓存时,就会直接返回数据,不需要进行后面的查询,这个效率很高。

  • 但是实际工作中 不推荐使用查询缓存 ,因为查询缓存往往是弊大于利;
    查询缓存的失效非常频繁,只要是对表中一个进行更新,整个查询缓存会被清空。因此很可能你之前缓存的数据,还没到使用呢,就被清空了。
    对于更新频繁的表(库)来说,缓存的命中率非常低。除非有一张静态表,更新频率很低,比如一张配置表,很少修改其中的配置,这张表才适合使用查询缓存。

MySQL 有一个 按需使用 的配置 。 可以将参数 query_cache_type 设置成 DEMAND ,这样对于默认的 SQL语句都不会使用查询缓存。对于确定要用查询缓存的语句可以显示的使用 query_cache 指定;

select QUERY_CACHE * from T where id = 10;

注意: MySQL 8.0 版本直接将查询缓存删掉了,8.0 版本以后没有查询缓存了。

  • 分析器

如果没有命中查询缓存,就开始正真执行语句了。首先MySQL需要知道你要做什么,因此会根据SQL语句进行解析;

  • 分析器会首先进行 词法分析,MySQL 把输入的多个字符串和空格识别出来,每个字符串是什么,代表什么。
    MySQL 把 select 这个关键字识别出来,然后把 T 分析成 表名 Tid 识别成 列ID
  • 然后进行 语法分析 ,根据词法分析的结构,语法分析会根据语法规则,判断输入的语句是否符合 MySQL语法。
    例如
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

一般语法错误会提示第一个出现错误的位置,所以要关注的是 use near 后面的。

  • 优化器

在开始执行之前,还要经过优化器的处理;
优化器是在表中有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联的时候,决定各表的连接顺序。
例如:

select * from a inner join b on a.id = b.id where a.name = 'tom' and b.class = 3;
  • 既可以先取出 a 表中 name = Tom 的数据然后根据 ID 连接 b 表取出 class = 3 的数据。
  • 还可以 先取出 b 表中 class = 3 的数据然后根据 ID 连接 a 表 取出 name = tom 的数据。
    两者的查询结果是一样的,但是执行效率是不一样的。优化器的作用就是选择哪一种执行方案;

优化器阶段完成后,这个语句的执行方案就确定了,然后就是执行器阶段。

  • 执行器

MySQL 通过分析器知道了要做什么,通过优化器知道了怎么做,然后就是执行器阶段了,开始正真执行SQL语句。

开始执行的时候,首先判断一下当前用户有没有这张表的查询权限,如果没有权限就会报错;

mysql> select * from T where ID=10;

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

如果有权限,就打开表继续执行。打开表的时候,会根据表的引擎定义,去使用这个引擎的接口;
如这个表 T 中,id 列没有索引,执行器的执行流程是这样的。

  • 调用 InnoDB 引擎的接口取出表的第一行,判断ID是不是 10 如果不是则跳过,如果是则把这行保存到结果集中;
  • 调用引擎接口调用 '下一行' ,重复相同的逻辑,直到取出表的最后一行;
  • 执行器将上述遍历过程中所有满足条件的行,组成记录集作为结果集返回给客户端。
    至此这条 查询 SQL 就执行完了。

对于有索引的表,逻辑也差不多。第一次调用的是 取出满足条件的第一行 这个接口,然后循环取出 满足条件的下一行 这个接口,这些接口都是引擎定义好的。

在慢查询日志中会看到 rows_examined 的字段,表示这个查询扫描了多少行。这个值就是执行器每次调用接口后累加的。

一个问题:
在以下查询语句中, T 表中不存在字段 k ,会报错 Unknown column ‘k’ in ‘where clause ,请根据上文回答,这个错误是哪个阶段报出的?

select * from T where k = 10;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容