vlambda博客
学习文章列表

查询语句在mysql的历程

select xxx,xxx,xxx from xxx where xxx=xxx,xxx=xxx

程序的日常开发中,我们经常会写到各种各样的简单的,复杂的查询sql语句。mysql也总是能把我们想要的结果返回给我们,但是你知道这个查询在mysql内部的一个执行过程么?

今天就带领大家简单看一看一个查询语句在mysql中的执行过程吧。

查询语句在mysql的历程


Mysql体系结构

要想知道一个查询语句在mysql的执行过程,首先先了解下mysql的体系架构吧Mysql体系结构如图所示查询语句在mysql的历程从图中可以看出mysql的体系结构分为了三层,连接层,SQL层和存储引擎层,但实际连接层和SQL层又统一称为SERVER层。所以mysql其实是由SERVER层存储引擎层组成的。下面就带领大家一层一层的来认识mysql架构。


当我们在代码中编写好一条sql后,执行代码,请求就会发送到mysql,与mysql建立连接,这个时候我们最先遇到了mysql的连接层:

连接层

应用程序访问mysql,先通过接口(如ODBC,JDBC等)来与mysql建立连接。连接层包含通信协议,线程处理,用户名密码认证三个部分。

  • 通信协议:该部分主要是来判断客户端的版本和mysql服务端是否兼容。

  • 线程处理:该部分将请求过来的sql语句分配到一个线程中处理,一条sql一个线程,一个线程又是一个逻辑CPU,不会在多个逻辑CPU之间进行切换。

  • 用户名密码认证:该部分就是看你配置或者输入的用户名,密码,host以及端口是否授权允许连接mysql服务端。

所以连接层的核心作用其实就是验证你是否能够进行接下来的操作,要是可以进行,ok继续往下走,要是不行,mysql就会对你说 拜拜走好不送。

好不容易我们通过了连接层,证明了我们可以进行接下来的操作了,但是你以为这样就完了么?并没有,在连接层这里,我们通过了用户名密码认证,同时也会将该账号所拥有的库表读写权限等信息读取到,并且这个连接断开重连前,我们所有的操作都将依赖于这个时候读到的权限。

也就是说哪怕这个时候我们用另一个连接给当前账号修改了权限,这个账号的权限也不受影响。只有在重新建立新的连接后才会使用新的权限设置。

这些完成后,我们的查询语句来到了sql层。

SQL层

SQL层包含权限判断,查询缓存,解析器,预处理,查询优化器,缓存和执行计划七个部分

  • 权限判断:判断当前用户是否有权限操作某个库某个表。

  • 查询缓存:查询缓存通过Query Cache进行操作,如果数据在Query Cache中,则直接返回结果给客户端。

  • 查询解析器:查询解析器针对SQL语句进行解析,判断语法是否正确。

  • 预处理器 :预处理器对解析器无法解析的语义进行处理。

  • 优化器:优化器对SQL进行改写和相应的优化,并生成最优的执行计划,就可以调用程序的API接口,通过存储引擎层访问数据。

语句达到sql层后,mysql会先查询缓存,看之前是否执行过这条sql,之前执行的语句及结果会以key-value的形式被缓存在内存中,key是查询的sql语句,value是查询到的结果。如果mysql在缓存中获取到了这个key,就会将这个value直接返回给客户端不再进行接下来的操作。如果不在的话,mysql就会继续向下执行,查询到结果后将结果缓存起来。

( 其实是不建议使用mysql的缓存的,mysql的缓存失效频率非常之高,而且MySQL 8.0 版本也直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了)

当缓存中没有我们要查询的数据时,优化器(代表查询解析器和预处理器)就会开始工作了。优化器首先会先进行词义分析,检测出你输入的sql语句都有什么,又代表什么,试图揣测你的意图。揣测完你的意图后,优化器会进行语法分析,判断你输入的内容是否符合mysql的语法要求。

ok,语法没有问题,mysql也知道了你的意图,接下来mysql就要看怎么处理才可以最快速的拿到你想要的结果,这个时候优化器就要派上用场了。当你他要查询的表里面有多个索引的时候,优化器要决定使用哪个索引;或者在一个语句有多表关联(join)的时候,优化器要决定各个表的连接顺序。

(优化器有的时候挺笨的,需要我们强行矫正)

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

执行器开始正式执行前,会先校验你的权限。没有的话mysql会返回如下错误

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

如果权限校验OK,就会打开表开始执行,打开表的时候,执行器就会获取到这张表定义的存储引擎,然后调用该引擎提供的接口拿到需要的数据返回给客户端。


至此,一条查询语句在mysql中的一个完整的流程就走完了。

刚刚上文中提到了存储引擎,下面的内容对存储引擎做一个简单的介绍。

存储引擎

目前mysql数据库及其分支主要的存储引擎有InnoDB,MyISAM,Memory,blackhole,TokuDB和MariaDB columnstore。下表为主要的存储引擎的特性对比

存储引擎名称 特点 应用场景
Innodb 支持事务、行锁,支持MVCC多版本并发控制,并发性高 应用于OLTP业务系统
MyISAM 不支持事务,表锁,MYSQL8.0后彻底被废弃,并发很低,资源利用率也低 应用于OLAP业务系统,建议在生产环境中尽量少用该存储引擎
Memory 表中的数据都在内存中存放,不落地。支持Hash和Btree索引,数据安全性不高,读取速度快 应用于对数据安全性要求不高的环境下
TokuDB 归Percona公司所有,支持事务,支持压缩功能,高速写入功能(比Innodb快9倍),在线Online DDL,不产生索引碎片 应用于海量数据的存储场景下,空间占比低
MariaDB columnstore 列式存储引擎,高压缩功能 数据仓库,OLAP业务系统
blackhole 并不存储数据,数据写入时只写binlog 常用来做binlog转储或测试

Innodb和MyISAM是目前最主流的两个存储引擎,不过MyISAM在mysql8.0后被彻底废弃。两者之间的主要区别如下表

区别 Innodb MyISAM
事务的支持 支持事务 不支持事务
锁粒度 行锁 表锁
并发性 高并发 低并发
构成结构和缓存机制 数据和所以文件都存储在.ibd文件里,并且都是缓存在内存里 数据文件的扩展名为.MYD 索引文件的扩展名是.MYI 只缓存索引文件 不缓存数据文件
select count(*) 需要全表扫描,统计所有行数 只需要从计算器中读出保存好的行数即可