MySQL专题(一)

MySQL

MySQL索引数据结构推导

初学数据结构

二叉树 如果数据是单边增长的情况 那么出现的就是和链表一样的数据结构了,树的高度,大数据量相当于全量查询.

红黑树 在二叉树的基础上多了树平衡,也叫二叉平衡树,不像二叉树那样极端的情况会往一个方向发展,但是大数据量下树的高度还是问题.

B树(B-Tree) 在红黑树的基础上,每个节点可以存放多个数据来减小树的高度.

B+树(B+Tree) B树的变种,非叶子节点是会重复的,需要在叶子上面存放数据,并且在叶子节点有单向递增引用.

MySQL的索引是排好序的数据结构,MySQL的索引用的就是B+树(B+Tree)数据结构,但是在叶子节点存在双向引用

MySQL默认的一个节点16K的大小,可以通过show global status like "Innodb_page_size" 看到该值是16384,每次IO读取16K大小的数据,以索引列bigInt类型为例,大小8字节,每一条数据还有一个指向下一层的指针6字节,16384/(8+6)=1170,一个节点就大约可以存1170条数据.

以一个层高为3的树为例,叶子节点存放数据之后大小1KB,那么这个树可以存放 1170 *1170 *16 =21,902,400,大约2200万条数据.所以在这种千万级的表中通过主键索引查找一条数据,最多3次IO就可以找到一条数据.而很多时候树的根节点基本都是在内存中,所以多数时候只需要2次IO.

叶子节点之间也有双向指针连接,提高区间范围性能,范围查找,头尾也有双向指针.

主键索引

  1. 叶子节点存储整行数据
  2. 叶子节点之间双向引用
  3. 非叶子节点只存储索引(冗余)不存储其他数据

辅助索引

  1. 叶子节点存储主键ID
  2. 叶子节点之间双向引用
  3. 非叶子节点只存储索引(冗余)不存储其他数据

联合主键索引

  1. 叶子节点存储整行数据
  2. 叶子节点之间双向引用
  3. 非叶子节点只存储索引(冗余)不存储其他数据
  4. 索引排序是按照建索引的顺序进行排序
  5. 在查询的时候要符合最左前缀法则索引才会生效

联合索引

  1. 叶子节点存储主键ID
  2. 叶子节点之间双向引用
  3. 非叶子节点只存储索引(冗余)不存储其他数据
  4. 索引排序是按照建索引的顺序进行排序
  5. 在查询的时候要符合最左前缀法则索引才会生效

MySQL分析工具Explain

DROP TABLE IF EXISTS `actor`;
CREATE TABLE `actor` (
	`id` int(11) NOT NULL,
	`name` varchar(45) DEFAULT NULL,
	`update_time` datetime DEFAULT NULL,
	PRIMARY KEY (`id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;
	
INSERT INTO `actor` (`id`, `name`, `update_time`)
VALUES(1, 'a', '2017‐12‐2 2 15:27:18'), 
		(2, 'b', '2017‐12‐22 15:27:18'), 
		(3, 'c', '2017‐12‐22 15:27:18');
		
		
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (
	`id` int(11) NOT NULL AUTO_INCREMENT,
	`name` varchar(10) DEFAULT NULL,
	PRIMARY KEY (`id`),
	KEY `idx_name` (`name`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;
	
INSERT INTO `film` (`id`, `name`) 
VALUES (3,'film0'),(1,'film1'),(2,'film 2');


DROP TABLE IF EXISTS `film_actor`;
CREATE TABLE `film_actor` (
	`id` int(11) NOT NULL,
	`film_id` int(11) NOT NULL,
	`actor_id` int(11) NOT NULL,
	`remark` varchar(255) DEFAULT NULL,
	PRIMARY KEY (`id`),
	KEY `idx_film_actor_id` (`film_id`,
		`actor_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;
		
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`) VALUES (1,1,1), (2,1,2),(3,2,1);

explain中的列

explain select * from actor;

1、id列 id列的编号是select的序列号,有几个select就有几个id,并且id的顺序是按select出现顺序增长的;

2、select_type列 select_type表示对应行是简单还是复杂的查询;

1)simple:简单查询,查询不包含子查询和union

2)primary:复杂查询中最外层的 select 3)subquery:包含在 select 中的子查询(不在 from 子句中) 4)derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为 派生表(derived的英文含义) 用这个例子来了解 primary、subquery 和 derived 类型

#关闭mysql5.7新特性对衍 生表的合并优化
set session optimizer_switch='derived_merge=off'; 

explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;

#还原默认配置
set session optimizer_switch='derived_merge=on'; 

5)union:在 union 中的第二个和随后的 select

explain select 1 union all select 1;

3、table列 这一列表示 explain 的一行正在访问哪个表 当 from 子句中有子查询时,table列是 格式,表示当前查询依赖 id=N 的查 询,于是先执行 id=N 的查询。 当有 union 时,UNION RESULT 的 table 列的值为,1和2表示参与 union 的 select 行id。

4、type列 这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据行记录的大概 范围。

依次从最优到最差分别为: system > const > eq_ref > ref > range > index > ALL 一般来说,得保证查询达到range级别,最好达到ref

NULL:mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。 例如:在 索引列中选取最小值,可以单独查找索引来完成,不需要在执行时访问表

explain select min(id) from film;

const, system:mysql能对查询的某部分进行优化并将其转化成一个常量(可以看show warnings 的结果)。 用于 primary key 或 unique key 的所有列与常数比较时,所以表最多 有一个匹配行,读取1次,速度比较快。 system是const的特例,表里只有一条元组匹配时为 system

 #MYSQL5.7
 explain extended select * from (select * from film where id = 1) tmp;
 show warnings;
 
 #MYSQL8+
 explain select * from (select * from film where id = 1) tmp;
 show warnings;

eq_ref:primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合 条件的记录。这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种 type。

 explain select * from film_actor left join film on film_actor.film_id = film.id;

ref:相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要 和某个值相比较,可能会找到多个符合条件的行。

1) 简单 select 查询,name是普通索引(非唯一索引)

 explain select * from film where name = 'film1';

2) 关联表查询,idx_film_actor_id是film_id和actor_id的联合索引,这里使用到了film_actor 的左边前缀film_id部分。

 explain select film_id from film left join film_actor on film.id = film_actor.film_id;

range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定 范围的行。

explain select * from actor where id > 1;

index:扫描全表索引,这通常比ALL快一些。

explain select * from film;

ALL:即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索 引来进行优化了

 explain select * from actor;

5、possible_keys列 这一列显示查询可能使用哪些索引来查找。 explain时可能出现possible_keys列,而key为NULL的情况,这种情况是因为表中数据不多,MySQL认为索引对其帮助不大,选择了全表扫描。 如果该列是NULL,则没有相关的索引,这种情况下可以通过检查where查询语句中,是否可以创造一个适当的索引来提高查询性能,然后用explain来查看结果。

6、key列 这一列显示mysql实际采用哪个索引来优化对该表的访问。 如果没有使用索引,则该列是 NULL。 如果想强制mysql使用或忽视possible_keys列中的索引,在查询中使用 force index、ignore index。

7、key_len列 这一列显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。 举例来说,film_actor的联合索引 idx_film_actor_id 由 film_id 和 actor_id 两个int列组成, 并且每个int是4字节。通过结果中的key_len=4可推断出查询使用了第一个列:film_id列来执 行索引查找。

 explain select * from film_actor where film_id = 2;

key_len计算规则如下: * 字符串 * char(n):n字节长度 * varchar(n):2字节存储字符串长度,如果是utf-8,则长度 3n + 2 * 数值类型 * tinyint:1字节 * smallint:2字节 * int:4字节 * bigint:8字节 * 时间类型 * date:3字节 * timestamp:4字节 * datetime:8字节 * 如果字段允许为 NULL,需要1字节记录是否为 NULL

索引最大长度是768字节,当字符串过长时,MySQL会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

8、ref列 这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常 量),字段名(例:film.id)

9、rows列 这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。

10、Extra列 这一列展示的是额外信息。 常见的重要值如下: 1)Using index:使用覆盖索引

explain select film_id from film_actor where film_id = 1;

2)Using where:使用 where 语句来处理结果,查询的列未被索引覆盖

explain select * from actor where name = 'a';

3)Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围;

 explain select * from film_actor where film_id > 1;

4)Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行 优化的,首先是想到用索引来优化。

  1. actor.name没有索引,此时创建了张临时表来distinct
explain select distinct name from actor;

  1. film.name建立了idx_name索引,此时查询时extra是using index,没有用临时表
 explain select distinct name from film;

5)Using filesort:将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘 完成排序。这种情况下一般也是要考虑使用索引来优化的。

  1. actor.name未创建索引,会浏览actor整个表,保存排序关键字name和对应的id,然后排序name并检索行记录
 explain select * from actor order by name;

  1. film.name建立了idx_name索引,此时查询时extra是using index
explain select * from film order by name;

6)Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段时

 explain select min(id) from film;