一:一条查询语句的执行流程
1.1、连接
跟数据库建立连接,MySQL服务监听的端口默认是3306,客户端连接服务端的方式有很多。可以是同步的也可以是异步的,可以是长连接也可以是短连接,可以是TCP也可以
查看MySQL当前有多少个连接
show global status like 'Thread%';
客户端每产生一个连接或者一个会话,在服务端就会创建一个线程来处理。反过来, 如果要杀死会话,就是Kill线程
既然是分配线程的话,保持连接肯定会消耗服务端的资源。MySQL会把那些长时间 不活动的(SLEEP)连接自动断开
show global variables like 'wait timeout';—非交互式超时时间,如 JDBC 程序
show global variables like 'interactive timeout';"交互式超时时间,如数据库工具
这两个参数默认都是28800秒,8小时。
既然连接消耗资源,MySQL服务允许的最大连接数(也就是并发数)在5.7版本中默认是151个,最大可以设置成100000
可以通过:show variables like 'max connections';
参数级别说明
MySQL中的参数(变量)分为session和global级别,分别是在当前会话中生效和 全局生效。但是并不是每个参数都有两个级别,比如max_connections就只有全局级别。
当没有带参数的时候,默认是session级别,包括查询和修改,比如修改了一个参数以后,在本窗口査询已经生效,但是其他窗口不生效
所以,如果只是临时修改,建议修改session级别。 如果需要在其他会话中生效,必须显式地加上global参数。
1.2、查询缓存
MySQL内部自带了一个缓存模块, MySQL的缓存默认是关闭的。
show variables like 'query_cache%';
默认关闭的意思就是不推荐使用,为什么MySQL不推荐使用它自带的缓存呢? 主要是因为MySQL自带的缓存的应用场景有限:
所以缓存这一块,我们还是交给0RM框架(比如MyBatis默认开启了一级缓存), 或者独立的缓存服务,比如Redis来处理更合适。MySQL 8.0中,查询缓存已经被移除了。
1.3 、解析器和预处理器
为什么一条SQL语句能够被识别呢?假如随便执行一个字符串hello 服务器报错。
这个就是MySQL的Parser解析器和Preprocessor预处理模块。
这一步主要做的事情是对语句基于SQL语法进行词法和语法分析和语义的解析。
1.3.1.词法解析
第一步词法分析就是把一个完整的SQL语句打碎成一个个的单词。
比如一个简单的SQL语句:
select name from user where id = 1
它会打碎成8个符号,每个符号是什么类型,从哪里开始到哪里结束。
1.3.1.语法解析
第二步就是语法分析,语法分析会对SQL做一些语法检查,比如单引号有没有闭合, 然后根据MySQL定义的语法规则,根据SQL语句生成一个数据结构。这个数据结构我 们把它叫做解析树(select lex)。
如果写了一个语法完全正确的树,但是表或者字段不存在,还是在解析的时候报错,因为解析器处理之后,还有一个预处理器,它用来判断解析树的语义是否正确,也就是表名和字段名是否存在,预处理后生成一个新的解析树。
select * from hello;//表不存在
实际上还是在解析的时候报错,解析SQL的环节里面有个预处理器。
它会检査生成的解析树,解决解析器无法解析的语义。比如,它会检査表和列名是否存在,检査名字和别名,保证没有歧乂。预处理之后得到一个新的解析树
1. 4、查询优化(Query Optimizer)与查询执行计划
得到解析树之后,是不是执行SQL语句了呢?这里还有一个问题,一条SQL语句是不是只有一种执行方式?或者说数据库最终 执行的SQL是不是就是我们发送的SQL?
这个答案是否定的。一条SQL语句是可以有很多种执行方式的,最终返回相同的结果,他们是等价的。但是如果有这么多种执行方式,这些执行方式怎么得到的?最终选 择哪一种去执行?根据什么判断标准去选择?
这个就是MySQL的査询优化器的模块(Optimizer)。
査询优化器的目的就是根据解析树生成不同的执行计划(Execution Plan),然后选 择一种最优的执行计划,MySQL里面使用的是基于开销(cost)的优化器,最终采用开销最小的作为最终执行的方案。
优化器最终会把解析树变成一个查询执行计划,查询执行计划一个数据结构。这个执行计划不一定是最优的结果,因为 mysql 也有可能覆盖不到所有的执行计划。
1. 4.1、逻辑优化
这个阶段主要是使用关系代数对SQL语句做一些等价,使得SQL执行效率最高。
对条件表达式进行等价谓词重写、条件简化,对视图进行重写,对子查询进行优化,对连接语义进行了外连接消除、嵌套连接消除等
案例:
子查询优化:子查询是SQL语句中出现频率很高的类型,而且也是较为耗时的操作。而子查询可能会出现在SQL中的目标列、from子句、where子句、JOIN/ON子句、GROUPBY子句、Having子句等
子查询合并:在某些条件下,多个子查询能合并成一个子查询,这样就可以把多次表扫
描,多次连接操作减少为单次表扫描和单次连接。
SELECT * FROM t1 WHERE a1<10 AND ( EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=1)
OR EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND t2.b2=2) );
优化成:
SELECT * FROM t1 WHERE a1<10 AND ( EXISTS(SELECT a2 FROM t2 WHERE t2.a2<5 AND
(t2.b2=1 OR t2.b2=2) );
SELECT * FROM USERINFO WHERE name LIKE 'Abc%'
优化成:
SELECT * FROM USERINFO WHERE name >='Abc' AND name <'Abd'
前者针对LIKE这个谓词只能进行全表扫描,而在后者改造中如果name列有索引,转化后就变成索引范围内扫描
select * from t1 where age in (10,14,18)
优化成:
select * from t1 where age =10 or age=14 or age=18
IN转化OR规则后,执行效率是否能够提高,需要看数据库中对IN谓词是否只支持全表扫描,如果数据库对IN谓词
只支持全表扫描并且OR谓词中的表的age列上存在索引,那么转化后查询效率会得到提高
1. 4.2、物理优化
在生成逻辑查询计划后,查询优化器会进一步对查询树进行物理查询优化,
物理优化主要解决几个问题:
物理查询优化一般分为两种:
单表扫描代价
单表扫描代价,单表扫描需要从表上获取元组,直接关联到物理IO的读取,因此不同的单表扫描方式,会有不同的代价,通常有两种:
而单表扫描涉及到的算法有很多,比如
对于局部扫描,通常会采用索引实现少量数据的读取优化,这是一种随机读取数据的方式。虽然顺序扫描可能比读取许多行的索引扫描花费的时间少,但是如果顺序扫描被执行多次(比如嵌套循环连接的表)它的整体代价更大,而索引扫描访问的数据页比较少,而且这些也可能会被保存在数据缓冲区中,所以访问速度会更快。
至于最终采用哪种扫描方式,查询优化器会采用代价估算比较代价大小后再决定。
总的来说,对于单表扫描的代价,由于单表扫描需要把数据从存储系统中加载到内存,所以单表扫描的代价需要考虑IO的开销。
1. 4.3、查询优化器整体结构
1. 5、执行计划:
当一条SQL语句经过Optimizer优化器优化之后,会针对这条SQL产生一个执行计划,从这个执行计划中可以得知Mysql会如何执行这条SQL。
在Mysql中,提供了EXPLAIN关键字来查看指定SQL的执行计划,通过该计划,可以得到以下信息。
1. 6、Storage Engines
一条SQL语句经过解析和优化之后,最终还是需要从某一个地方获取数据,
那么:
这里就涉及到存储引擎(Storage Engines)的概念了
存储引擎是Mysql中才有的概念,它表示数据如何存储、如何提取、如何更新等具体的实现,不同存储引擎的底层实现方式不同,因此 不同的存储引擎提供的特性都 不一样,它们有不同的存储机制、索引方式、锁定水平等功能。
我们在不同的业务场景中对数据操作的要求不同,就可以选择不同的存储引擎来满 足我们的需求
1. 6.1、InnoDB(2个文件)
mysql 5.7中的默认存储引擎。InnoDB是一个事务安全(与ACID兼容)的MySQL 存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB行级锁(不升级 为更粗粒度的锁) ,适合经常更新的表,存在并发读写或者有事务处理的业务系统。只有在需要它不支持的特性时,才考虑使用其它存储引擎。
实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(Next-Key Locking)防止幻读。
主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,以减少基于主键的常见查询的I/O,因此对查询性能有很大的提升。 为了保持数据完整性, InnoDB还支持外键引用完整性约束。
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。
支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取
1. 6.2、MylSAM (3个文件)
应用范围比较小。表级锁定限制了读/写的性能,因此在Web和数据仓库配置中,
它通常用于只读或以读为主的数据分析项目 或者表比较小、可以容忍修复操作,则依然可以使用它。
特点:
怎么快速向数据库插入100万条数据?
我们有一种先用MylSAM插入数据,然后修改存储引擎为InnoDB的操作。
1. 6.3、Memory (1个文件)
将所有数据存储在RAM中,以便在需要快速查找非关键数据的环境中快速访问。这 个引擎以前被称为堆引擎。其使用案例正在减少;InnoDB及其缓冲池内存区域提供了一 种通用、持久的方法来将大部分或所有数据保存在内存中,而ndb custer为大型分布式数据集群提供了快速的键值查找。
特点:将表中的数据存储到内存中,读写的速度很快,但是数据库重启或者崩溃,数据会全部消失。只适合做临时表。
1. 6.4、CSV (3个文件)
它的表实际上是带有逗号分隔值的文本文件。csv表允许以CSV格式导入或转储数据, 以便与读写相同格式的脚本和应用程序交换数据。因为CSV表没有索引,所以通常在正 常操作期间将数据保存在innodb表中,并且只在导入或导出阶段使用csv表。
特点:不允许空行,不支持索引。格式通用,可以直接编辑,适合在不同数据库之 间导入导出
1. 6.5、Archive (2 个文件)
这些紧凑的未索引的表用于存储和检索大量很少引用的历史、存档或安全审计信息
特点:不支持索引,不支持update delete
1. 6.6、如何选择存储引擎?
MyISAM 和InnoDB 区别
1.7.执行引擎(Query Execution Engine)
OK,存储引擎分析完了,它是我们存储数据的形式,第二个问题,是谁使用执 行计划去操作存储引擎呢?
这就是我们的执行引擎,它利用存储引擎提供的相应的API来完成操作。为什么我们修改了表的存储引擎,操作方式不需要做任何改变,因为不同功能的存储引擎实现的API是相同的。最后把数据返回给客户端。