首先,数据是存储在MySQL服务端的,应用程序或者工具都是客户端,客户端想要读写数据,第一步得跟服务端建立连接。
MySQL内部自带了一个缓存模块,但是MySQL的缓存默认是关闭的,因为MySQL自带的缓存的应用场景有限,第一个是它要求SQL语句必须一模一样,例如中间多一个空格、字母大小写不同都被认为是不一样的SQL语句。
第二个是表里面的任何一条数据发生变化的时候,这张表所有的缓存都会失效,所以对于大量数据更新的应用,也不适合。
缓存这一块还是交给ORM框架,或者独立的缓存服务,比如Redis来处理更合适。
在MySQL8.0中,查询缓存已经被移除了。
可以使用这行语句来查看MySQL查询缓存的相关属性
show variables like 'query_cache%';
假如随便执行一个字符串,服务器报了一个1064的错:
[Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the rightsyntaxtousenear’penyuyan’atline1 它是怎么知道输入的内容是错误的?
这个就是MySQL的Parser解析器和Preprocessor预处理模块。
这一步主要做的事情是对SQL语句进行词法和语法分析和语义的解析
1.3.1.词法解析
词法分析就是把一个完整的SQL语句打碎成一个个的单词。
比如一个简单的SQL语句:
select name from user where id=1
它会打碎成8个符号,每个符号是什么类型,从哪里开始到哪里结束。
1.3.2.语法解析
第二步就是语法分析,语法分析会对SQL做一些语法检查,比如单引号有没有闭合,
然后根据MySQL定义的语法规则,根据SQL语句生成一个数据结构。
这个数据结构我们把它叫做解析树(select_lex)。
词法语法分析是一个非常基础的功能,编译器、搜索引擎如果要识别语句,必须也要有词法语法分析功能。
1.3.3 预处理器
如果写了一个词法和语法都正确的SQL,但是表名或者字段不存在,会在哪里报错?
是解析的时候报错还是执行的时候报错?
实际上还是在解析的时候报错,解析SQL的环节里面有个预处理器。
它会检查生成的解析树,解决解析器无法解析的语义。比如,它会检查表和列名是否存在,检查名字和别名,保证没有歧义。
预处理之后得到一个新的解析树。
1.4.1 什么是优化器?
解析树是一个可以被执行器认识的数据结构。
一条SQL语句是不是只有一种执行方式?或者说数据库最终执行的SQL是不是就是发送的SQL?
这个答案是否定的。一条SQL语句是可以有很多种执行方式的,最终返回相同的结果,他们是等价的。但是如果有这么多种执行方式,这些执行方式怎么得到的?最终选择哪一种去执行?根据什么判断标准去选择?
这个就是MySQL的查询优化器的模块(Optimizer)。
查询优化器的目的就是根据解析树生成不同的执行计划(ExecutionPlan),然后选择一种最优的执行计划,MySQL里面使用的是基于开销(cost)的优化器,那种执行计划开销最小,就用哪种。
可以使用这个命令查看查询的开销:
show status like 'Last_query_cost';
1.4.2 优化器可以做什么?
MySQL的优化器能处理哪些优化类型呢?
举两个简单的例子:
1、当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表(先访问哪张表)。
2、有多个索引可以使用的时候,选择哪个索引。
3、对于查询条件的优化,比如移除1=1 之类的恒等式,移除不必要的括号,表达式的计算,子查 询和连接查询的优化。
…
1.4.3. 优化器得到的结果
优化器最终会把解析树变成一个执行计划(execution_plans),执行计划也是一个数据结构。
当然,这个执行计划是不是一定是最优的执行计划呢?不一定,因为MySQL也有可能覆盖不到所有的执行计划。
我们怎么查看MySQL的执行计划呢?比如多张表关联查询,先查询哪张表?在执行查询的时候可能用到哪些索引,实际上用到了什么索引?
MySQL提供了一个执行计划的工具。我们在SQL语句前面加上EXPLAIN,就可以看到执行计划的信息。
EXPLAIN select cust_name from customer where cust_id=14;
如果要得到详细的信息,还可以用FORMAT=JSON。
1.5.1 存储引擎基本介绍
表在存储数据的同时,还要组织数据的存储结构,这个存储结构就是由我们的存储引擎决定的,所以我们也可以把存储引擎叫做表类型。
在MySQL里面,支持多种存储引擎,他们是可以替换的,所以叫做插件式的存储引擎。
1.5.2 查看存储引擎
查看数据库里面已经存在的表的存储引擎:
show table status from `ssm_crm`;
在MySQL里面,我们创建的每一张表都可以指定它的存储引擎,而不是一个数据库只能使用一个存储引擎。存储引擎的使用是以表为单位的(所以叫表类型)。而且,创建表之后还可以修改存储引擎。
默认情况下,每个数据库都有一个自己的文件夹,存储在服务器端,可以通过下面这条语句查看
show variables like 'datadir';
任何一个存储引擎都有一个frm文件,这个是表结构定义文件。
不同的存储引擎存放数据的方式不一样,产生的文件也不一样,innodb 是 1 个,
memory没有,myisam是两个。
1.5.3 存储引擎比较
我们可以用这个命令查看MySQL对存储引擎的支持情况:
show engines;
其中有存储引擎的描述和对事务、XA协议和savepoints的支持。
XA协议用来实现分布式事务(分为本地资源管理器,事务管理器)。
Savepoints用来实现子事务(嵌套事务)。创建了一个Savepoints之后,事务就可以回滚到这个点,不会影响到创建Savepoints之前的操作。
MyISAM(3个文件)
MySQL自带的存储引擎,由ISAM升级而来。
应用范围比较小。表级锁定限制了读/写的性能,因此在Web 和数据仓库配置中,它通常用于只读或以读为主的工作。
特点:
支持表级别的锁(插入和更新会锁表)。不支持事务。
拥有较高的插入(insert)和查询(select)速度。
存储了表的行数(count速度更快)。
(怎么快速向数据库插入100万条数据?我们有一种先用MyISAM插入数据,然后
修改存储引擎为InnoDB的操作。)
适合:只读之类的数据分析的项目。
InnoDB(2个文件)
mysql5.7中的默认存储引擎。 InnoDB是一个事务安全(与ACID兼容)的MySQL存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB行级锁(不升级为更粗粒度的锁)和Oracle风格的一致非锁读提高了多用户并发性和性能。InnoDB将用户数据存储在聚集索引中,以减少基于主键的常见查询的I/O。为了保持数据完整性,InnoDB还支持外键引用完整性约束。
特点:
支持事务,支持外键,因此数据的完整性、一致性更高。
支持行级别的锁和表级别的锁。
支持读写并发,写不阻塞读(MVCC)。
特殊的索引存放方式,可以减少IO,提升查询效率。
适合:经常更新的表,存在并发读写或者有事务处理的业务系统。
Memory(1 个文件)
将所有数据存储在RAM中,以便在需要快速查找非关键数据的环境中快速访问。这个引擎以前被称为堆引擎。其使用案例正在减少; InnoDB及其缓冲池内存区域提供了一种通用、持久的方法来将大部分或所有数据保存在内存中,而ndbcluster为大型分布式数据集提供了快速的键值查找。
特点:
把数据放在内存里面,读写的速度很快,但是数据库重启或者崩溃,数据会全部消失。只适合做临时表。
将表中的数据存储到内存中。
默认使用哈希索引。
CSV(3个文件)
它的表实际上是带有逗号分隔值的文本文件。 csv表允许以csv格式导入或转储数据,以便与读写相同格式的脚本和应用程序交换数据。因为csv 表没有索引,所以通常在正常操作期间将数据保存在innodb表中,并且只在导入或导出阶段使用csv表。
特点:不允许空行,不支持索引。格式通用,可以直接编辑,适合在不同数据库之
间导入导出。
不同的存储引擎提供的特性都不一样,它们有不同的存储机制、索引方式、锁定水平等功能。
我们在不同的业务场景中对数据操作的要求不同,就可以选择不同的存储引擎来满足我们的需求,这个就是MySQL支持这么多存储引擎的原因。
1.5.4 如何选择存储引擎
如果对数据一致性要求比较高,需要事务支持,可以选择InnoDB。
如果数据查询多更新少,对查询性能要求比较高,可以选择MyISAM。
如果需要一个用于查询的临时表,可以选择Memory。
如果所有的存储引擎都不能满足你的需求,并且技术能力足够,可以根据官网内部
手册用C语言开发一个存储引擎:
https://dev.mysql.com/doc/internals/en/custom-engine.html
按照这个开发规范,实现相应的接口,给执行器操作。
也就是说,为什么能支持这么多存储引擎,还能自定义存储引擎,表的存储引擎改了对Server访问没有任何影响,就是因为大家都遵循了一定了规范,提供了相同的操作接口。
执行器,或者叫执行引擎,它利用存储引擎提供的相应的API来完成操作。最后把数据返回给客户端,即使没有结果也要返回。
总结:一条SQL语句的执行流程大致为,客户端先与MySQL服务器建立连接,然后,发送一条查询语句,如果MySQL开启了缓存的话,会将SQL语句存到缓存中,然后,解析器进行词法和语法的解析,接着预处理器会进行再次检查,比如检查表名和列名是否存在,没有问题的话,优化器会对SQL语句进行优化,生成一个执行计划,交给执行器执行SQL,执行器调用存储引擎,存储引擎读取磁盘数据,将查询结果交给执行器,执行器再将查询结果反馈给客户端或缓存。
如有不对或不足之处,欢迎指正,谢谢!