1.MySQL是什么
MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理。本篇文章主要会对MySQL的执行流程进行解析,所以你需要MySQL的使用经验。
2.MySQL的执行流程是怎样的
MySQL的执行流程可以理解为,一条sql语句从程序到数据库再把查询到的数据返回给用户,这一过程中发生了什么。
3.一条sql语句的完整执行流程
4.首先客户端需要连接上MySQL服务器,
MySQL是支持多种通信协议的,可以使用同步/异步的方式,支持长连接/短连接。 这里我们拆分来看。
第一个是通信类型,MySQL的通信类型分为同步和异步。
同步通信的特点:
1、同步通信依赖于被调用方,受限于被调用方的性能。也就是说,应用操作数据库, 线程会阻塞,等待数据库的返回。
2、一般只能做到一对一,很难做到一对多的通信。
异步通信的特点:
1、异步可以避免应用阻塞等待,但是不能节省SQL执行的时间。
2、如果异步存在并发,每一个SQL的执行都要单独建立一个连接,避免数据混乱。 但是这样会给服务端带来巨大的压力(一个连接就会创建一个线程,线程间切换会占用大量CPU资源)。另外异步通信还带来了编码的复杂度,所以一般不建议使用。如果要异步,必须使用连接池,排队从连接池获取连接而不是创建新连接。
所以说大部分都是用同步连接
第二个是连接方式:长连接或者短连接
MySQL既支持短连接,也支持长连接。短连接就是操作完毕以后,马上close 掉。 长连接可以保持打开,减少服务端创建和释放连接的消耗,后面的程序访问的时候还可 以使用这个连接。一般我们会在连接池中使用长连接。
保持长连接会消耗内存。长时间不活动的连接,MySQL服务器会断开。 默认最大长连接时间是8小时。
在5.7版本中MySQL服务默认允许151连接,最大可以设置成16384(2^14)。
第三个是通信方式:单工、半双工和全双工
单工:在两台计算机通信的时候,数据的传输是单向的。生活中的类比:遥控器。
半双工: 在两台计算机之间,数据传输是双向的,你可以给我发送,我也可以给你发送,但是在这个通讯连接里面,同一时间只能有一台服务器在发送数据,也就是你要给我发 的话,也必须等我发给你完了之后才能给我发。生活中的类比:对讲机。
全双工: 数据的传输是双向的,并且可以同时传输。生活中的类比:打电话。
MySQL使用了半双工的通信方式
要么是客户端向服务端发送数据,要么是服务端向客户端发送数据,这两个动作不能 同时发生。所以客户端发送SQL语句给服务端的时候,(在一次连接里面)数据是不能分成小块发送的,不管你的SQL语句有多大,都是一次性发送。
比如我们用MyBatis动态SQL生成了一个批量插入的语句, 插入10万条数据, values 后面跟了一长串的内容,或者where条件in里面的值太多,会出现问题。 这个时候我们必须要调整MySQL服务器配置max_allowed_packet参数的值(默认 是4M),把它调大,否则就会报错。
另一方面,对于服务端来说,也是一次性发送所有的数据,不能因为你已经取到了想要的数据就中断操作,这个时候会对网络和内存产生大量消耗。 所以,我们一定要在程序里面避免不带limit 的这种操作,比如一次把所有满足条件 的数据全部查出来,一定要先count一下。如果数据量的话,可以分批查询
5.建立连接后,接下来就是查询缓存
MySQL中自带了一个缓存模块,缓存的作用我们应该很清楚了,把数据以KV的形式放到内存里面,可以加快数据的读取速度,也可以减少服务器处理的时间。
key = sql语句
value = 数据
但是MySQL中的缓存是默认不开启的,因为他的缓存必须要求sql语句完全一致,多了一个空格都查不出来,并且只要这张表的数据发生改变,该表的所有缓存都会失效,所以该缓存的作用微乎其微。
所以缓存这一块,我们还是交给ORM框架(比如MyBatis默认开启了一级缓存),或者独立的缓存服务,比如Redis来处理更合适。在MySQL 8.0中,查询缓存已经被移除了
6.语法解析和预处理
这一步主要做的事情是对语句基于SQL语法进行词法和语法分析和语义的解析。
1.词法解析
词法分析就是把一个完整的SQL语句打碎成一个个的单词。 比如一个简单的SQL语句:
它会打碎成8个符号,每个符号是什么类型,从哪里开始到哪里结束。
2.语法解析
语法分析会对SQL做一些语法检查,比如单引号有没有闭合, 然后根据MySQL定义的语法规则,根据SQL语句生成一个数据结构。这个数据结构我 们把它叫做解析树(select_lex)。
任何数据库的中间件,比如Mycat,Sharding-JDBC(用到了Druid Parser),都 必须要有词法和语法分析功能,在市面上也有很多的开源的词法解析的工具(比如LEX 、Yacc)。
3.预处理器
它会检查生成的解析树,解决解析器无法解析的语义。比如,它会检查表和列名是否存在,检查名字和别名,保证没有歧义。 预处理之后得到一个新的解析树。
7.查询优化(Query Optimizer)与查询执行计划
得到解析树之后,并不是直接执行sql语句。
1.什么是查询优化器
一条SQL语句是可以有很多种执行方式的,最终返回相同的结果,他们是等价的。但是如果有这么多种执行方式,就需要用到查询优化器,
查询优化器的目的就是根据解析树生成不同的执行计划(ExecutionPlan),然后选择一种最优的执行计划,MySQL里面使用的是基于开销(cost)的优化器,那种执行计 划开销最小,就用哪种。
2.查询优化器功能
MySQL的优化器能处理哪些优化类型呢?
举两个简单的例子:
1、当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表
2、有多个索引可以使用的时候,选择哪个索引。
实际上,对于每一种数据库来说,优化器的模块都是必不可少的,他们通过复杂的 算法实现尽可能优化查询效率的目标。
但是优化器也不是万能的,并不是再垃圾的SQL语句都能自动优化,也不是每次都能选择到最优的执行计划。
3.优化器是怎么得到执行计划的?
expanded_query是优化后的SQL语句。
considered_execution_plans里面列出了所有的执行计划。
4.优化器得到的结果
优化器最终会把解析树变成一个查询执行计划,查询执行计划是一个数据结构。
当然,这个执行计划是不是一定是最优的执行计划呢?不一定,因为MySQL也有可能覆盖不到所有的执行计划。
我们怎么查看MySQL的执行计划呢?比如多张表关联查询,先查询哪张表?在执行查询的时候可能用到哪些索引,实际上用到了什么索引?
MySQL提供了一个执行计划的工具。我们在SQL语句前面加上EXPLAIN,就可以看到执行计划的信息。
8.存储引擎
1.存储引擎做什么
首先在关系型数据库中,数据是放在一个表里面的
我们可以把这个表理解成Excel电子表格的形式。所以我们的表在存储数据的同时, 还要组织数据的存储结构,这个存储结构就是由我们的存储引擎决定的,所以我们也可 以把存储引擎叫做表类型。
在MySQL里面,我们创建的每一张表都可以指定它的存储引擎,而不是一个数据库 只能使用一个存储引擎。存储引擎的使用是以表为单位的。而且,创建表之后还可以修改存储引擎。
默认情况下,每个数据库有一个自己文件夹,任何一个存储引擎都有一个frm文件,这个是表结构定义文件。
2.常见存储引擎
MyISAM 和InnoDB 是我们用得最多的两个存储引擎,在 MySQL 5.5 版本之前, 默认的存储引擎是MyISAM,它是MySQL自带的。我们创建表的时候不指定存储引擎, 它就会使用MyISAM作为存储引擎。
5.5版本之后默认的存储引擎改成了InnoDB,它是第三方公司为MySQL开发的。 为什么要改呢?最主要的原因还是InnoDB 支持事务,支持行级别的锁,对于业务一致 性要求高的场景来说更适合
3.MyISAM
应用范围比较小。表级锁定限制了读/写的性能,因此在Web 和数据仓库配置中, 它通常用于只读或以读为主的工作。
特点:
支持表级别的锁(插入和更新会锁表)。不支持事务。
拥有较高的插入(insert)和查询(select)速度。
存储了表的行数(count速度更快)
适合:只读之类的数据分析的项目。
4.InnoDB
mysql5.7中的默认存储引擎。 InnoDB是一个事务安全(与ACID兼容)的MySQL 存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB行级锁(不升级 为更粗粒度的锁)和Oracle风格的一致非锁读提高了多用户并发性和性能。InnoDB将用户数据存储在聚集索引中,以减少基于主键的常见查询的I/O。为了保持数据完整性,InnoDB还支持外键引用完整性约束
特点:
支持事务,支持外键,因此数据的完整性、一致性更高。
支持行级别的锁和表级别的锁。
支持读写并发,写不阻塞读(MVCC)。
特殊的索引存放方式,可以减少IO,提升查询效率。
适合:经常更新的表,存在并发读写或者有事务处理的业务系统。
5.如何选择存储引擎
如果对数据一致性要求比较高,需要事务支持,可以选择InnoDB。
如果数据查询多更新少,对查询性能要求比较高,可以选择MyISAM
如果所有的存储引擎都不能满足你的需求,并且技术能力足够,可以根据官网内部 手册用C语言开发一个存储引擎
9.执行引擎
为什么我们修改了表的存储引擎,操作方式不需要做任何改变?
因为不同功能的存 储引擎实现的API是相同的。 最后把数据返回给客户端,即使没有结果也要返回。
10.MySQL 体系结构总结
1、 Connector:用来支持各种语言和SQL的交互,比如PHP,Python,Java的 JDBC;
2、 Management Serveices & Utilities:系统管理和控制工具,包括备份恢复、 MySQL复制、集群等等;
3、 Connection Pool:连接池,管理需要缓冲的资源,包括用户密码权限线程等等
4、 SQL Interface:用来接收用户的SQL命令,返回用户需要的查询结果
5、 Parser:用来解析SQL语句
6、 Optimizer:查询优化器;
7、 CacheandBuffer:查询缓存,除了行记录的缓存之外,还有表缓存,Key缓 存,权限缓存等等
8、 Pluggable Storage Engines:插件式存储引擎,它提供API给服务层使用, 跟具体的文件打交道
11. 架构分层
总体上,我们可以把MySQL分成三层,跟客户端对接的连接层,真正执行操作的服 务层,和跟硬件打交道的存储引擎层(参考MyBatis:接口、核心、基础)。
12.接下来重点分析InnoDB
1.缓冲池 Buffer Pool
首先,InnnoDB的数据都是放在磁盘上的,InnoDB操作数据有一个最小的逻辑单位,叫做页(索引页和数据页)。我们对于数据的操作,不是每次都直接操作磁盘,因为磁盘的速度太慢了。 InnoDB使用了一种缓冲池的技术,也就是把磁盘读到的页放到一 块内存区域里面。这个内存区域就叫Buffer Pool。
下一次读取相同的页,先判断是不是在缓冲池里面,如果是,就直接读取,不用再次访问磁盘。
修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候, 我们把它叫做脏页。InnoDB里面有专门的后台线程把Buffer Pool的数据写入到磁盘, 每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。
2. InnoDB 内存结构和磁盘结构
Buffer Pool 主要分为 3 个部分: Buffer Pool、Change Buffer、Adaptive Hash Index,另外还有一个(redo)log buffer。
1、Buffer Pool:
Buffer Pool缓存的是页面信息,包括数据页、索引页。
Buffer Pool默认大小是128M(134217728字节),可以调整。
如果页面写满了,InnoDB 用 LRU 算法来管理缓冲池(链表实现,不是传统的LRU,分成了young和old),经过淘汰的 数据就是热点数据。
2.change buffer
如果这个数据页不是唯一索引,不存在数据重复的情况,也就不需要从磁盘加载索 引页判断数据是不是重复(唯一性检查)。这种情况下可以先把修改记录在内存的缓冲 池中,从而提升更新语句(Insert、Delete、Update)的执行速度
最后把 Change Buffer 记录到数据页的操作叫做 merge。什么时候发生 merge? 有几种情况:在访问这个数据页的时候,或者通过后台线程、或者数据库shut down、 redo log写满时触发。
3.(redo)Log Buffer
如果BufferPool里面的脏页还没有刷入磁盘时,数据库宕机或者重启,这些数据丢失。如果写操作写到一半,甚至可能会破坏数据文件导致数据库不可用。
为了避免这个问题, InnoDB把所有对页面的修改操作专门写入一个日志文件,并且 在数据库启动时从这个文件进行恢复操作(实现crash-safe)——用它来实现事务的持 久性。
当然redo log也不是每一次都直接写入磁盘,在Buffer Pool里面有一块内存区域 (Log Buffer)专门用来保存即将要写入日志文件的数据,默认16M,它一样可以节省 磁盘IO。
什么时候会把log pool的数据写到磁盘中
最后一点,进行redo log操作时候,要进行IO对数据直接写道磁盘也需要IO,为什么还要多此一举?
因为IO的方式不同,顺序IO消耗的时间短,而随机IO消耗的时间长。
13.磁盘结构
1.系统表空间 system tablespace
InnoDB系统表空间包含InnoDB数据字典和双写缓冲区,ChangeBuffer和Undo Logs
数据字典:由内部系统表组成,存储表和索引的元数据(定义信息)。
双写缓冲区:保存数据库一个页的副本,如果出现了 写入失效,就用页的副本来还原这个页,然后再应用redolog。DoubleWrite也是又两部分组成,内存和磁盘。
Undo Logs:undolog(撤销日志或回滚日志)记录了事务发生之前的数据状态(不包括select) 。 如果修改数据时出现异常,可以用undo log来实现回滚操作(保持原子性)。
redo Log和undo Log与事务密切相关,统称为事务日志。
请记住那些表、区、页和行仅仅是逻辑结果他们都属于general tablespaces
2. Binlog
binlog以事件的形式记录了所有的DDL和DML语句(因为它记录的是操作而不是 数据值,属于逻辑日志),可以用来做主从复制和数据恢复。
跟redo log不一样,它的文件内容是可以追加的,没有固定大小限制。 在开启了binlog功能的情况下,我们可以把binlog导出成SQL语句,把所有的操 作重放一遍,来实现数据的恢复。
binlog的另一个功能就是用来实现主从复制,它的原理就是从服务器读取主服务器的binlog,然后执行一遍
3.独占表空间 file-per-table tablespaces
我们可以让每张表独占一个表空间。这个开关通过innodb_file_per_table设置,默 认开启。
4.通用表空间 general tablespaces
通用表空间也是一种共享的表空间,跟ibdata1类似。 可以创建一个通用的表空间,用来存储不同数据库的表,数据路径和文件可以自定义