MySQL架构与SQL执行流程(一)
版权所有,转载请注明出处!
本篇博客主要针对有有MySQL使用经验的读者。
做技术就要知道技术的实现原理,如果只是停留在一个会用的层面,我觉得不是一个合格的程序员。(你肯定会说,我不要你觉得,我只要我觉得! +_+ ~~~)。
1.MySQL发展史
首先我们先看一下MySQL的发展史,如下表所示:(你们自己看就行了,反正我也是这块内容从其他地方抄过来的。)
时间 | 里程碑 |
1996年 |
MySQL 1.0 发布。它的历史可以追溯到 1979 年,作者 Monty 用 BASIC 设计 的一个报表工具。 |
1996年10月 |
3.11.1 发布。MySQL 没有 2.x 版本。 |
2000年 |
ISAM 升级成 MyISAM 引擎。MySQL 开源。 |
2003年 |
MySQL 4.0 发布,集成 InnoDB 存储引擎。 |
2005年 |
MySQL 5.0 版本发布,提供了视图、存储过程等功能。 |
2008年 |
MySQL AB 公司被 Sun 公司收购,进入 Sun MySQL 时代。 |
2009 年 |
Oracle 收购 Sun 公司,进入 Oracle MySQL 时代。 |
2010年 |
MySQL 5.5 发布,InnoDB 成为默认的存储引擎。 |
2016年 |
发布 8.0.0 版本。为什么没有 6、7?5.6 可以当成 6.x,5.7 可以当 成 7.x。 |
常用的版本应该是5.X和现在的8.X,至于8.X的一些新特性,就不做太多的介绍,大家可以自行百度一下。
2.一条查询SQL语句是如何执行的?
首先上个图,从这个流程示意图中我们可以看出,当一个客户端或者是APP向MySQL数据库发送一条查询语句的时候(整个过程我们使用问答的形式进行阐述。):
1.首先与MySQL建立连接,连接必然涉及到通信协议,那么MySQL支持哪些协议呢?
在MySQL给出的官方文档中,有写到MySQL主要支持的协议有:
①Unix Socket
②TCP/IP
另外还有命名管道(Named Pipes)和内存共享(Share Memory)的方式,这两种通信方式只能在 Windows 上面使用,一般用得比较少。这里不做过多介绍,因为不是重点。
当连接建立之后,如果长时间不进行操作,MySQL会通过设置的连接断开时间终止已经建立的连接。
使用以下命令可以查询MySQL设置的默认连接失效时间:
(因为我使用的是客户端的博客园所以代码没办法格式化,涉及到代码部分就用蓝色字体表示吧。)
show global variables like 'wait_timeout'; //查看当前数据库非交互式 (如JDBC)连接失效时长
show global variables like 'interactive_timeout'; //查看当前数据库交互式 (如数据库工具)连接失效时长
我们可以看出数据库默认的连接时长是28800秒,即8个小时。
这里顺便说一下global和session的区别:global是基于全局的设置,而session是基于当前会话的设置。
那么数据库应该怎样查看已经建立的连接?我们可以使用以show status命令进行查看:
show global status like 'Thread%';
Threads_cached: 缓存中的线程连接数。
Threads_connected: 当前打开的连接数。
Threads_created:为处理连接创建的线程数。
Threads_running:非睡眠状态的连接数,通常指并发连接数。
每产生一个连接或者一个会话,在服务端就会创建一个线程来处理。反过来,如果要杀死会话,就是 Kill 线程。
有了连接数,我们怎么知道每个连接的状态?可以使用show processlist命令进行查看。
show processlist;
标红线的地方这里,因为我设置了读写分离,所以会多出来两个连接,这个可以忽略。
为了效果更明显,我们后面的演示,都用Navicat Premium工具类来进行演示。
一些常见状态说明,可以参考MySQL官方文档给出的定义。
我们顺便思考一个问题:MySQL中允许最大的连接数是多少?
在5.7的版本之后,默认允许的连接数是151,最大可设置成16384,(2^14)。
因为我装的数据的版本是5.5版本,所以演示效果不是151,我们可以使用
select version();命令来查询当前版本号
修改的命令为:
set global max_connections = 1000;
重启后有效。
2.建立连接之后,首先经过的是MySQL中查询缓存。
MySQL 内部自带了一个缓存模块。
缓存的作用我们应该很清楚了,把数据以 KV 的形式放到内存里面,可以加快数据的读取速度,也可以减少服务处理的时间。但是为什么我们会对MySQL的缓存比较陌生,甚至没有听说过。
因为MySQL的缓存默认是关闭的。可以使用以下语句进行查询:
默认关闭就是官方不推荐使用,为什么呢?因为MySQL的缓存对SQL的拼写相似度要求比较高,比如多一个空格或者少一个空格,查询缓存都会失败。我们可以基于ORM框架或者是redis进行数据的缓存。
当然最新的MySQL8已经把查询缓存这块内容移除掉了。
3.语法解析和预处理
我们会有一个疑问,为什么我的一条SQL能被识别?加入我随便执行一个字符串,MySQL去报错了呢?MySQL是怎样知道我们输入的内容是错的?
这里就涉及到了MySQL的Parser解析器和Preprocessor预处理模块。
这里主要是对语句进行SQL语法进行词法和语法分析和语义解析。
这块内容不做过多讲解,因为我对这块东西也不是特别感兴趣,大概就是一个套用正则解析的过程。
词法分析就是把一个完整的 SQL 语句打碎成一个个的单词。
第二步就是语法分析,语法分析会对 SQL 做一些语法检查,比如单引号有没有闭合,
然后根据 MySQL 定义的语法规则,根据 SQL 语句生成一个数据结构。这个数据结构我
们把它叫做解析树(select_lex)。
解析器可以分析语法,但是它怎么知道数据库里面有什么表,表里面有什么字段呢?
实际上还是在解析的时候报错,解析 SQL 的环节里面有个预处理器。
它会检查生成的解析树,解决解析器无法解析的语义。比如,它会检查表和列名是
否存在,检查名字和别名,保证没有歧义。
预处理之后得到一个新的解析树。
4.查询优化与查询执行计划
得到解析树之后,是不是执行 SQL 语句了呢?
这里我们有一个问题,一条 SQL 语句是不是只有一种执行方式?或者说数据库最终执行的 SQL 是不是就是我们发送的 SQL?
这个答案是否定的。一条 SQL 语句是可以有很多种执行方式的,最终返回相同的结果,他们是等价的。但是如果有这么多种执行方式,这些执行方式怎么得到的?最终选
择哪一种去执行?根据什么判断标准去选择?
这个就是 MySQL 的查询优化器的模块(Optimizer)。
查询优化器的目的就是根据解析树生成不同的执行计划(Execution Plan),然后选择一种最优的执行计划,MySQL 里面使用的是基于开销(cost)的优化器,那种执行计
划开销最小,就用哪种。
可以使用这个命令查看内存的开销:
show status like 'Last_query_cost';
执行计划的查看主要用到了 explain这个命令,我们在执行SQL的时候使用这个命令可以查看到sql的执行计划明细,尤其在查看命中索引的时候,这个命令起到很大的作用。
5.存储引擎
在 MySQL 里面,支持多种存储引擎,他们是可以替换的,所以叫做插件式的存储引擎。
我们怎样查看数据库的存储引擎呢?
可以通过执行 show table status from `dbname`;来查看数据库使用的存储引擎。
我们还可以通过show engines;命令来查看当前数据库支持的那些存储引擎。
我们常用的两种存储引擎MyISAM和InnoDB这两种,而且在MySQL数据库中只有InnoDB存储引擎支持事务。
那么存储引擎是怎样存储数据的?
我们可以使用show variables like 'datadir';来查看数据存储引擎的存放目录。
任何一个存储引擎都有一个 frm 文件,这个是表结构定义文件。
不同的存储引擎存放数据的方式不一样,产生的文件也不一样,innodb 是 1 个, memory 没有,myisam 是两个。
MyISAM和InnoDB存储引擎有什么区别呢?
InnoDB: mysql 5.7 中的默认存储引擎。InnoDB 是一个事务安全(与 ACID 兼容)的 MySQL存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB 行级锁(不升级为更粗粒度的锁)和 Oracle 风格的一致非锁读提高了多用户并发性和性能。InnoDB 将用户数据存储在聚集索引中,以减少基于主键的常见查询的 I/O。为了保持数据完整性,InnoDB 还支持外键引用完整性约束。
特点:
支持事务,支持外键,因此数据的完整性、一致性更高。
支持行级别的锁和表级别的锁。
支持读写并发,写不阻塞读(MVCC)。
特殊的索引存放方式,可以减少 IO,提升查询效率。
适合:经常更新的表,存在并发读写或者有事务处理的业务系统。
MyISAM:应用范围比较小。表级锁定限制了读/写的性能,因此在 Web 和数据仓库配置中,它通常用于只读或以读为主的工作。
特点:
支持表级别的锁(插入和更新会锁表)。不支持事务。
拥有较高的插入(insert)和查询(select)速度。
存储了表的行数(count 速度更快)。
(怎么快速向数据库插入 100 万条数据?我们有一种先用 MyISAM 插入数据,然后修改存储引擎为 InnoDB 的操作。)
适合:只读之类的数据分析的项目。