MySQL高级——存储引擎、索引介绍、EXPLAIN性能分析
好的博客:MySQL高级
一、MySQL逻辑架构
Connectors
:指的是不同语言中与SQL的交互。Connection Pool
:管理缓冲用户连接,线程处理等需要缓存的需求。MySQL数据库的连接层。Management Serveices & Utilities
:系统管理和控制工具。备份、安全、复制、集群等等。。SQL Interface
:接受用户的SQL命令,并且返回用户需要查询的结果。Parser
:SQL语句解析器。Optimizer
:查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求query,根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果。For Example:select uid,name from user where gender = 1;
这个select
查询先根据where
语句进行选取,而不是先将表全部查询出来以后再进行gender
过滤;然后根据uid
和name
进行属性投影,而不是将属性全部取出以后再进行过滤。最后将这两个查询条件联接起来生成最终查询结果。Caches & Buffers
:查询缓存。Pluggable Storage Engines
:存储引擎接口。MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎(注意:存储引擎是基于表的,而不是数据库)。File System
:数据落地到磁盘上,就是文件的存储。
MySQL数据库和其他数据库相比,MySQL有点与众不同,主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其他的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需求选择合适的存储引擎。
逻辑架构分层
- 连接层:最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于
tcp/ip
的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL
的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。 - 服务层:MySQL的核心服务功能层,该层是MySQL的核心,包括查询缓存,解析器,解析树,预处理器,查询优化器。主要进行查询解析、分析、查询缓存、内置函数、存储过程、触发器、视图等,select操作会先检查是否命中查询缓存,命中则直接返回缓存数据,否则解析查询并创建对应的解析树。
- 引擎层:存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
- 存储层:数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
二、存储引擎
show engines;
命令查看MySQL支持的存储引擎。
mysql> show engines;
show variables like 'default_storage_engine%';
查看当前数据库正在使用的存储引擎。
mysql> show variables like 'default_storage_engine%';
+------------------------+--------+
| Variable_name | Value |
+------------------------+--------+
| default_storage_engine | InnoDB |
+------------------------+--------+
1 row in set (0.01 sec)
InnoDB和MyISAM对比:
对比项 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整张表,不适合高并发操作 | 行锁,操作时只锁某一行,不对其他行有影响,适合高并发操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,対内存要求较高,而且内存大小対性能有决定性影响 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | Y | Y |
阿里所使用的的存储引擎:
三、索引介绍
3.1 SQL性能下降的原因
-
查询语句写的差。
-
索引失效:索引建了,但是没有用上。
单值索引:
CREATE INDEX idx_表名_字段名 ON 表名(字段名);//此时使用该字段名进行查询时效率更高
符合索引:
CREATE INDEX idx_表名_字段名1字段名2... ON 表名(字段名1, 字段名2 ...);
-
关联 查询太多
join
(设计缺陷或者不得已的需求)。 -
服务器调优以及各个参数的设置(缓冲、线程数等)。
3.2 常见通用的join查询
3.2.1 SQL执行顺序
手写顺序:
随着 Mysql 版本的更新换代,其优化器也在不断的升级,优化器会分析不同执行顺序产生的性能消耗不同而动态调整执行顺序
下面是经常出现的查询顺序:
对于mysql而言,机读的时候从from开始:
3.2.2 七种JOIN理论
/* 1 */
SELECT <select_list> FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key;
/* 2 */
SELECT <select_list> FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key;
/* 3 */
SELECT <select_list> FROM TableA A INNER JOIN TableB B ON A.Key = B.Key;
/* 4 */
SELECT <select_list> FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key WHERE B.Key IS NULL;
/* 5 */
SELECT <select_list> FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL;
/* 6 */
SELECT <select_list> FROM TableA A FULL OUTER JOIN TableB B ON A.Key = B.Key;
/* MySQL不支持FULL OUTER JOIN这种语法 可以改成 1+2 */
SELECT <select_list> FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key
UNION
SELECT <select_list> FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key;
/* 7 */
SELECT <select_list> FROM TableA A FULL OUTER JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL OR B.Key IS NULL;
/* MySQL不支持FULL OUTER JOIN这种语法 可以改成 4+5 */
SELECT <select_list> FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key WHERE B.Key IS NULL;
UNION
SELECT <select_list> FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL;
3.3 索引简介
3.3.1 索引是什么?
-
MySQL官方对索引的定义为:索引(INDEX)是帮助MySQL高效获取数据的数据结果。 从而可以获得索引的本质:索引是排好序的快速查找数据结构。 所以索引会影响到MySQL查找(WHERE的查询条件)和排序(ORDER BY)两大功能
-
除了数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。
下图就是一种可能的索引方式示例:
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址。为了加快 Col2 的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用 二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录 -
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。
-
我们平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种数据结构的索引之外,还有哈希索引(Hash Index)等。
3.3.2 索引的优势和劣势
优势:
- 查找:类似大学图书馆的书目索引,提高数据检索的效率,降低数据库的IO成本。
- 排序:通过索引対数据进行排序,降低数据排序的成本,降低了CPU的消耗。
劣势:
- 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
- 虽然索引大大提高了查询速度,但是同时会降低表的更新速度,例如对表频繁的进行
INSERT
、UPDATE
和DELETE
。因为更新表的时候,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。 - 索引只是提高效率的一个因素,如果MySQL有大数据量的表,就需要花时间研究建立最优秀的索引。
3.3.3 索引的分类
索引分类:
- 单值索引:一个索引只包含单个列,一个表可以有多个单列索引。
- 唯一索引:索引列的值必须唯一,但是允许空值。主键就是一种唯一索引。
- 复合索引:一个索引包含多个字段。
索引的创建、删除和查看:
/* 基本语法 */
/* 1、创建索引 [UNIQUE]可以省略,省略表示单值索引,加上表示唯一索引*/
/* 如果只写一个字段就是单值索引,写多个字段就是复合索引 */
CREATE [UNIQUE] INDEX indexName ON tabName(columnName(length));
/* 2、删除索引 */
DROP INDEX [indexName] ON tabName;
/* 3、查看索引 */
/* 加上\G就可以以列的形式查看了 不加\G就是以表的形式查看 */
SHOW INDEX FROM tabName \G;
使用ALTER
命令来为数据表添加索引:
/* 1、该语句添加一个主键,这意味着索引值必须是唯一的,并且不能为NULL */
ALTER TABLE tabName ADD PRIMARY KEY(column_list);
/* 2、该语句创建索引的键值必须是唯一的(除了NULL之外,NULL可能会出现多次) */
ALTER TABLE tabName ADD UNIQUE indexName(column_list);
/* 3、该语句创建普通索引,索引值可以出现多次 */
ALTER TABLE tabName ADD INDEX indexName(column_list);
/* 4、该语句指定了索引为FULLTEXT,用于全文检索 */
ALTER TABLE tabName ADD FULLTEXT indexName(column_list);
3.3.4 索引数据结构
索引数据结构:
BTree
索引。- Hash索引。
- Full-text全文索引。
- R-Tree索引。
BTree
索引检索原理:
3.3.5 适合创建和不适合创建索引的情况
适合索引的场景:
- 主键自动建立主键索引(唯一 + 非空)。
- 频繁作为查询条件的字段应该创建索引。
- 查询中与其他表关联的字段,外键关系建立索引。
- 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度。
- 查询中统计或者分组字段(group by也和索引有关)。
不适合索引的场景:
- 记录太少的表。
- 经常增删改的表。
- 频繁更新的字段不适合创建索引。
- Where条件里用不到的字段不创建索引。
- 过滤性不好的不适合建索引(重复性较高,比如国籍、性别之类的字段)
四、EXPLAIN性能分析
4.1 EXPLAIN简介
EXPLAIN是什么?
EXPLAIN:SQL的执行计划,使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的。
EXPLAIN怎么使用?
语法:
explain
+SQL
。
Explain 执行后返回的信息:
EXPLAIN能干嘛?
可以查看以下信息:
id
:表的读取顺序。select_type
:数据读取操作的操作类型。table
:查询的访问类型possible_keys
:哪些索引可以使用。key
:哪些索引被实际使用。ref
:表之间的引用。rows
:每张表有多少行被优化器查询。
Extra
:其他的额外重要的信息。
4.2 EXPLAIN字段
-
id:表的读取和加载顺序
id是select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序值有以下三种情况:
id
相同,执行顺序由上至下。
id
不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行。
id
相同不同,同时存在。永远是id大的优先级最高,id相等的时候顺序执行。
-
select_type:数据查询的类型,主要是用于区别,普通查询、联合查询、子查询等的复杂查询
SIMPLE
:简单的SELECT
查询,查询中不包含子查询或者UNION
。PRIMARY
:查询中如果包含任何复杂的子部分,最外层查询则被标记为PRIMARY
。SUBQUERY
:在SELECT
或者WHERE
子句中包含了子查询。DERIVED
:在FROM
子句中包含的子查询被标记为DERIVED(衍生)
,MySQL会递归执行这些子查询,把结果放在临时表中。UNION
:如果第二个SELECT
出现在UNION
之后,则被标记为UNION
;若UNION
包含在FROM
子句的子查询中,外层SELECT
将被标记为DERIVED
。UNION RESULT
:从UNION
表获取结果的SELECT
。
-
table:表的来源
table表示这个数据是基于哪张表的 -
type:访问类型排列
type 是查询的访问类型。是较为重要的一个指标,结果值从最好到最坏依次是:system
>const
>eq_ref
>ref
>range
>index
>ALL
。除了ALL
没有用到索引,其他级别都用到索引了。system
:表只有一行记录(等于系统表),这是const
类型的特例,平时不会出现,这个也可以忽略不计。const
:表示通过索引一次就找到了,const
用于比较primary key
或者unique
索引。因为只匹配一行数据,所以很快。如将主键置于where
列表中,MySQL就能将该查询转化为一个常量。eq_ref
:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
ref
:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行, 然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
range
:只检索给定范围的行,一般就是在WHERE
语句中出现了BETWEEN
、< >
、in
等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引树的某一点,而结束于另一点,不用扫描全部索引。
index
:Full Index Scan
,全索引扫描,index
和ALL
的区别为index
类型只遍历索引树。也就是说虽然ALL
和index
都是读全表,但是index
是从索引中读的,ALL
是从磁盘中读取的。
ALL
:Full Table Scan
,没有用到索引,全表扫描。
一般来说,得保证查询至少达到
range
级别,最好达到ref
。 -
possible_keys
possible_keys
:显示可能应用在这张表中的索引,一个或者多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。 -
key
key
:实际使用的索引。如果为NULL
,则没有使用索引或索引失效。查询中如果使用了覆盖索引,则该索引仅仅出现在key
列表中。
-
key_len
key_len
:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
key_len
显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len
是根据表定义计算而得,不是通过表内检索出的。在不损失精度的情况下,长度越短越好。
key_len计算规则:https://blog.csdn.net/qq_34930488/article/details/102931490 -
ref
ref
:显示索引的哪一列被使用了,如果可能的话,是一个常 数。哪些列或常量被用于查找索引列上的值。 -
rows
rows
:根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数。 -
Extra
Extra
:包含不适合在其他列中显示但十分重要的额外信息。
Using filesort
:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作成为"文件内排序"。
Using temporary
:使用了临时表保存中间结果,MySQL在対查询结果排序时使用了临时表。常见于排序order by
和分组查询group by
。临时表対系统性能损耗很大。
Using index
:表示相应的SELECT
操作中使用了覆盖索引,避免访问了表的数据行,效率不错!如果同时出现Using where
,表示索引被用来执行索引键值的查找;如果没有同时出现Using where
,表明索引用来读取数据而非执行查找动作。
Using where
:表明使用了WHERE
过滤。Using join buffer
:使用了连接缓存。impossible where
:WHERE
子句的值总是false,不能用来获取任何元组。