前言
你好,我是码叔叔。做软件开发已近第6个年头了,也算一名互联网老兵了。作为后端开发,数据存储肯定是我们必须要掌握的基本技能。在学习及使用存储服务过程中,我遇到了许多问题,走过不少弯路。同时,在不断的学习,受挫、总结的过程中也学习了很多。
记得在从业第一年的时候,项目经理让我设计一个评论的系统(就是那种文章评论),从数据库设计到代码编写。毫无编程经验的“小弱鸡”赶鸭子上架,直接开搞。记得第一版评审时,项目经理点评数据库设计时,只说了一句“回去看看数据库设计三范式”。word天呐,现在想想当时真是啥也不懂。后来工作两年后,我来到了一家人工智能公司。在一次修改存储过程上线后,整个数据库出现了死锁,公司很多业务线受到影响。当时真的想找个楼顶跳一跳,但是一想,还有那么多想象不到的快乐没有开发,怎么能一走了之呢?
很多时候遇到问题,也只能Google一下,学到的知识碎片化很严重,曾经遇到过这样一个问题,一个10毫秒就能执行完的sql,有时候居然要100毫秒。当时怎么也想不通,直到有一次掉到一个山崖下,发现一个雪猿,雪猿拿给我一本书,我一看,正是那本“九阴真经MySQL版”。哈哈,这下终于可以搞明白了。于是我非常努力的练功,终于有所小成。
我知道很多小伙伴也想全貌的了解MySQL,在这个系列课程中,我把九阴真经MySQL精华提炼出来,同时做了很多基础知识的补充,可以说是笔记式的技术文章。
有需要提升MySQL知识的同学,可以关注一下,但是需要大家耐心把所有章节阅读完成。欢迎大家和我一起学习,如果发现文中有疑问或者错误也请指出。
在这里,有一份基础篇知识目录,大家先从整体上了解一下。
- 01 九阴真经MySQL版:一条查询语句如何执行的?
- 02 九阴真经MySQL版:一条更新语句如何执行的?
- 03 九阴真经MySQL版:事务隔离及MVCC多版本控制
- 04 九阴真经MySQL版:深入理解索引
- 05 九阴真经MySQL版:MySQL全局锁和表锁
- 06 九阴真经MySQL版:行锁对MySQL性能的影响
- 07 九阴真经MySQL版:再述事务隔离
学习完基础篇后,还会有实践篇,后续内容更加精彩。
鸣谢:丁奇大佬分享的《MySQL实战45讲》
MySQL架构
学习一门“新的”知识,我们的学习路线最好是从全貌到细微。这一章的内容是一条查询SQL是如何执行的?似乎和MySQL架构并没有多大关系,其实不然。请看下面这张MySQL架构图:
我们在这里先混个眼熟,不需要掌握每个组件的含义。下面我们会把MySQL拆解出若干个“零件”,在这个过程中希望你能对MySQL有更深入的理解,遇到问题时,能直指问题的本质。在此,我们简化MySQL逻辑架构,大致可以分为以下两层:
-
MySQL Server层: MySQL server层包括连接器、查询缓存、分析器、优化器、执行器等。包含了mysql大多数核心服务功能,内置函数(日期、时间、数学)、存储过程、触发器、视图等都在这一层。
-
引擎层: 引擎层负责数据得存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。Mysql在5.5版本以后默认的存储引擎采用InnoDB。
指定引擎
create table student(
id int(11) auto_increment,
name varchar(255) not null,
age int(11) not null,
address varchar(255) ,
primary key(id),
index idx_name(name)
)engine = innoDB default charset=utf8;
下面是我绘制的MySQL逻辑架构,从这张图中你可以看到一条查询SQL在MySQL各组件中是如何运行的。
连接器
负责来自客户端的连接、获取用户权限、维持和管理连接。一个用户成功建立连接之后,即使管理员对权限做了修改,也不会影响这个连接的权限。
- 长连接:建立连接后,如果客户端有请求,则一直使用同一个连接。如果客户端长时间没有发送请求,连接器会自动断开连接,这个时间由参数wait_time控制,默认8小时。有时候我们会发现,mysql内存激增,这是因为mysql在执行过程中临时使用的内存是管理在连接里面的,只有当连接断开时,资源才会得到释放。MySQL在5.7版本后,可以使用mysql_reset_connection来重新初始化资源,这个过程不需要重新建立连接及权限认证。
- 短链接:每次查询都会建立一条连接,查询结束后销毁连接。频繁的建立连接、断开连接会消耗很多性能。
查询缓存
创建连接后,查询逻辑就会来到查询缓存。之前执行过的查询会以K、V格式直接缓存在内存中。查询语句作为key,查询结果作为value,如果你当前的查询语句在缓存中,则直接把缓存中的value作为返回值返回。如果不在查询缓存中,则继续执行下面得流程。
MySQL 8.0版本已近将整个查询缓存删掉了,为什么?
查询缓存得更新是非常快的,只要有对表得更新操作,那么关于表得所以查询缓存都会清空。除非业务表是长期不需要更新,可以使用查询缓存。因此可能费劲把数据存起来,还没使用就被清空了。
分析器
-
词法分析:分析器会对输入得sql做词法分析,MySQL需要识别字符串分别是什么,代表什么意思。
MySQL从输入得“select”判断这是一个查询语句,需要把T识别为“表名T”,把字符串识“id”识别为“列id”
-
语法分析:做完词法分析后,MySQL会做语法分析,根据词法分析得结果,语法分析器会根据语法规则,判断你输入的sql是否满足MySQL语法。
SELEC * FROM retail_order_redundancy WHERE id = 12
1064 - 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 'SELEC * FROM retail_order_redundancy WHERE id = 12' at line 1
我们可能会经常看到上面这种错误(select书写错误),这里就是由词法分析器爆出来的。
优化器
优化器在表中有多个索引得时候决定使用哪个索引,或者多张表join时决定表得连接顺序。例如:
select * from t1 join t2 on t1.id = t2.id where t1.c = 10 and t2.d = 20
方案一:我们可以先从t1表中获取c=10得记录然后通过id关联t2,继而判断t2.d是否等于20
方案二:也可以先从t2表中先获取d值为20得记录,然后关联t1,再判断t1得c是否为10。
两种方案得到的结果是一样的,但是性能可能会差别很多,而优化器得作用就是会选择使用哪种方案。关于优化器怎么选择索引,有没有可能会选错,会在后面的文章做详细得说明。
执行器
sql经过分析器知道想要什么,经过优化器知道了怎么做。接下来就到了执行器,开始执行sql语句。
开始执行之前,要判断一下当前用户对表是否有查询权限,如果没有就会返回权限错误。如果有权限,执行器就会根据表得定义引擎,去使用这个引擎提供得接口。
例如selec * from T where c = 10(假如C字段无索引),执行流程是这样的
- 调用innoDB引擎接口取这个表得第一行,判断ID是否等于10,如果是则加入结果集中,如果不是则跳过。
- 调用引擎取“下一行”接口,重复相同得判断逻辑,直到取到表得最后一行。
- 执行器将上述遍历过程中得所有满足条件得结果作为记录集返回给客户端。
假设C字段有索引,执行过程如下:
- 调用引擎接口“满足条件得第一行”,如果有数据则加入结果集中。如果没有,直接返回空结果集。
- 如果第一步取到了值,那么就循环调用接口“满足条件的下一行”
- 执行器将上述遍历过程中得所有满足条件得结果作为记录集返回给客户端。
至此整个执行就全部结束了。可以看到有无索引对查询来说还是有很大差别的,无索引情况下,需要遍历整表,有索引情况下在索引树中取数,如果第一步“满足条件的第一行”没有数据,整个查询结束。
小结
在这一章节中,我们了解了MySQL整体架构设计包括了server层、引擎层。server层包含了连接器、查询缓存、分析器、优化器以及执行器等,MySQL引擎层有多种不同的实现,MySQL5.5版本以后默认使用innoDB引擎。
现在你应该了解一条查询语句的执行逻辑了,首先客户端和连接器建立连接,创建连接后,查询请求首先在查询缓存中查询,如果查询到就返回结果,查询不到则来到分析器,分析器分析后知道这个请求要干什么,分析器分析完成后,优化器优化查询逻辑,到此MySQL已近知道了要干什么以及怎么干,执行器上场了,执行器调用引擎层接口,返回查询得结果集。
问题
执行 select * from T where k = 1,如果表T中不存在k字段,那么肯定会报错“Unknown column ‘k’ in ‘where clause’”,这个报错是在我们上面提到的哪一个阶段爆出来的呢?评论下方留言,下一期解答。