mysql学习笔记-索引2

----------------------------------索引-----------------------------------
mysql官方对索引的定义:

索引(Index)是帮助mysql高效获取数据的数据结构===>索引的本质:索引是数据结构

索引是排好序的快速查找数据结构

一般索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上

平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引,其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是B+树索引,统称索引。当然,除了B+树赭红类型的索引外,还有哈希索引(hash index)等

数据本身之外,数据库还维护着一个满足特定查找算法的数据结构。这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法。这种数据结构就是索引。

索引的优势

①类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本
②通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗

索引的劣势

①实际上索引也是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引也要占用空间
②虽然索引大大提高查询速度,但同时降低了更新表的速度,如对表进行insert update 和detele.因为更新表。mysql不仅要保存数据还要保存一下索引文件每次更新添加索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
③索引只是提高效率的一个因素,如果mysql有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询,索引建立不是一朝一夕的。

索引分类
单值索引

一个索引只包含单个列,一个表可以有多个单列索引

唯一索引

索引列的值必须唯一,但允许有空值

复合索引

一个索引包含多个列

基本语法
①创建

create [unique] index indexName on tableName(columnName...);
alter tableName add [unique] index [indexName] on (columnName...)

②删除索引

drop index [indexName] on tableName;

③查看索引

show index from tableName \G;

④使用alter命令添加数据表的索引

alter table tableName add primary key(column_list); //添加主键 ,意味着索引值必须是唯一的,且不能为null
alter table tableName add unique index_name(column_list); //创建的索引值必须是唯一的(除了null外,null可能出现多次)
alter table tableName add index index_name(column_list); //添加普通索引,索引值可出现多次
alter table tableName add  fullText index_name(column_list); //该语句指定了索引为fullText,用于全文索引

mysql索引结构
①BTree索引–索引原理
②Hash索引
③full-text全文索引
④R-Tree索引

哪些情况需要创建索引
①主键自动建立唯一索引
②频繁作为查询条件的字段应该创建索引
③查询中与其他表关联的字段,外键关系建立索引
④频繁更新的字段不适合创建索引,因为每次更新除了更新记录还会更新索引,加重IO负担
⑤where条件里用不到的字段不创建索引
⑥单键/组合索引选择问题:高并发下倾向于创建组合索引
⑦查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
⑧查询中统计或者分组字段需要创建索引

不创建索引的情况
①表记录太少,通常再300万条数据之前,mysql查询速度还是可以的,此时不需要创建索引,300万数据之后性能开始下降
②经常增删改的表:建立索引提高查询速度,同时却降低更新表的速度,如对表进行insert , update,delete,同时因为更新表时,mysql不仅要保存数据,还要保存索引文件
③注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果

假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
索引的选择性是指,索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99,一个索引的选择性越接近1,这个索引的效率就越高。

mysql性能分析
①Mysql Query Optimizer(mysql查询优化器)

1 mysql中有专门负责优化select语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的query提供他认为最优的执行计划(他认为最优的数据检索方式不一定是DBA认为最优的,这部分最耗费时间)
2 当客户端向mysql请求一条query,命令解析器模块完成请求分类,区别出是select并转发给mysql query optimizer时,mysql query optimizer首先会对整条query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对query中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件,结构调整等。然后分析query中Hint信息(如果有),看显示Hint信息是否可以完全确定该query的执行计划。如果没有Hint或Hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据query进行写相应的计算分析,然后再得出最后的执行计划

②mysql常见性能瓶颈:

1 CPU:CPU饱和一般发生在数据装入内存或从磁盘上读取数据的时候
2 IO: 磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
3 服务器硬件的性能瓶颈:top , free , iostat 和 vmstat 来查看系统的性能状态

③explain关键字
1 是什么

使用explain关键字可以模拟优化器执行sql查询语句,从而知道mysql是如何处理你的sql语句的。分析查询语句或表结构的性能瓶颈(结合mysql官网查看)
https://dev.mysql.com/doc/refman/5.7/en/execution-plan-information.html

2 能做什么

①表的读取顺序
②数据读取操作的操作类型
③那些索引可以使用
④那些索引被实际使用
⑤表之前的引用
⑥每张表有多少行被优化器查询

3如何使用

explain + sql语句
执行计划包含的信息

+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+

名字段解释
一 id
①select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
②三种情况:
id相同,执行顺序由上至下
id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
id相同不同,同时存在,id值越大,优先级越高,越先被执行,id相同仍然按照从上至下执行的原则 derived表示衍生 表示衍生的表其id为2

二 select_type
①有哪些

1 SIMPLE
2 PRIMARY
3 SUBQUERY
4 DERIVED
5 UNION
6 UNION RESULT

②查询的类型,主要是用于区别普通查询,联合查询,子查询等的复杂查询

1 SIMPLE 简单的select查询,查询中不包含子查询或union
2 PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY
3 SUBQUERY 在select或where列表中包含了子查询
4 DERIVED 在from列表中包含的子查询被标记为DERIVED(衍生) mysql会递归执行这些子查询,把结果放在临时表里
5 UNION 若第二个select出现在UNION之后,则被标记为UNION 若UNION包含早在from子句的子查询中,外层select将被标记为DERIVED
6 UNION RESULT 从UNION表获取结果的select

type
①访问类型排列

type显示的是访问类型,是较为重要的一个指标。结果值从好到坏依此是
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref

②显示查询使用了何种类型

1 system : 表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计。(system经过尝试就算建一张一个字段的表,也出现不了system的查询类型,mysql版本为5.7,就算将id作为主键,并最为查询条件(where后) type类型就是const)
2 const : 表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,mysql就能将该查询转换为一个常量
3 eq_ref : 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
4 ref 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以它应该属于查找和扫描的混合体
5 range : 只检索给定范围的行,使用一个索引来选择行。key列显示使用那个索引。一般就是在where语句中出现了between , < , < , in等的查询。这种范围扫描索引扫描比全表扫描好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
6 index : full index scan, index与all区别为index类型只遍历索引树。这通常比all快,因为索引文件通常比数据文件小。(也就是虽然all和index都是读全表,但index是从索引中读取,而all是从硬盘中读)
7 all : full table scan ,将遍历全表以找到匹配的行
备注:一般来说,得保证查询至少达到range级别,最好能达到ref.

possible_keys

显示可能应用在这张表中的索引,一个或多个。
查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

key

实际使用的索引。如果为null,则没有使用索引
查询中若使用了覆盖索引,则该索引仅出现在key列表中

key-len

表示索引中使用的字符,可通过该列计算查询中使用的索引长度。在不损失精确性的情况下,长度越短越好
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不得通过表内检索出的

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

rows

根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

extra

包含不适合在其他列中显示但十分重要的额外信息
1 Using filesort : 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序读取,mysql中无法利用索引完成的排序操作称为“文件排序”;(使用group by的字段最好和所建索引的顺序和数目一致,否则容易产生文件内排序filesort 以及 使用临时表)
2 Using temporary : 使用临时表保存中间结果,mysql在对查询结果排序时使用临时表。常见于排序order by 和 分组查询 group by;
3 Using index : 表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错
如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
4 Using where 表明使用了where过滤
5 using join buffer : 使用了连接缓存,说明配置文件中的join buffer可以调大一些
6 impossible where : where子句的值总是false(相当于一个字段既要求是男 又要求同时是女),不能用来获取任何元组
7 select tables optimized away : 在没有group by 子句的情况下,基于索引优化min/max操作或者对于mysql存储引擎优化count(*)操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即完成优化
8 distinct : 优化distinct操作,在找到第一个匹配的元组后即停止找同样值的操作

索引优化案例
一 单表索引所优化案例

# 建表
drop table if exists article;
create table article(
	id int primary key auto_increment ,
	author_id int not null,
	category_id int not null,
	views int not null,
	comments int not null,
	title varchar(255) not null,
	content text not null
);

#插入数据
insert into article (author_id,category_id,views,comments,title,content) values
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');

##查询
mysql> select * from article;
+----+-----------+-------------+-------+----------+-------+---------+
| id | author_id | category_id | views | comments | title | content |
+----+-----------+-------------+-------+----------+-------+---------+
|  1 |         1 |           1 |     1 |        1 | 1     | 1       |
|  2 |         2 |           2 |     2 |        2 | 2     | 2       |
|  3 |         1 |           1 |     3 |        3 | 3     | 3       |
+----+-----------+-------------+-------+----------+-------+---------+

#查询category_id为1 且 comments > 1 的情况下,views最多的article_id
mysql> select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-----------+
| id | author_id |
+----+-----------+
|  3 |         1 |
+----+-----------+

#使用explain分析这条sql,可以看到type为ALL,意味着全表扫描,extra信息显示using filesort,文件内排序,可以看出这是一条性能低下的sql,因此需要优化
mysql> explain select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | article | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where; Using filesort |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+

#通常在where中查询的字段需要建立索引,先尝试为where后面用到的三个字段建立联合索引,看看效果如何
create index article_ccv on article(category_id,comments,views);

#再来看一下分析结果,可以看到type变成了range,范围查找,解决了之前全表扫描的问题,并且使用到了索引,但是仍然可以看到extra中依然使用到了filesort,因此依然没有解决文件内排序的问题。现在是建的联合索引,comments在联合索引的中间,此sql中comments为一个范围,这样的话views就不能使用索引进行查找了,因此考虑跳过comments字段,重新建立category_id和views的联合索引,看一下是否性能更加优越
mysql> explain select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+------+----------+---------------------------------------+
| id | select_type | table   | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra                                 |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+------+----------+---------------------------------------+
|  1 | SIMPLE      | article | NULL       | range | article_ccv   | article_ccv | 8       | NULL |    1 |   100.00 | Using index condition; Using filesort |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+------+----------+---------------------------------------+

#删除之前建的联合索引
drop index article_ccv on article;

#重建联合索引
create index article_cv on article(category_id,views);

#再次查看sql的分析情况,可以看到type变成了ref(非唯一性索引扫描),这个级别效率已经很高了,使用到了索引,然后extra列的信息显示已经没有在使用filesort了,说明已经不在进行文件内排序,因此性能较之前的sql相比有了很大的提升
mysql> explain select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | article | NULL       | ref  | article_cv    | article_cv | 4       | const |    2 |    33.33 | Using where |
+----+-------------+---------+------------+------+---------------+------------+---------+-------+------+----------+-------------+

二 两表索引优化案例

#建表
drop table if exists `class`;
create table `class`(
	id int primary key auto_increment,
	card int not null
);

drop table if exists books;
create table books(
	bookId int primary key auto_increment,
	card int not null
);

#插入数据 两表均插入20条随机数据,这样的话能保证会出现左表有的右边没有,反之亦然。
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `class`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));
insert into `books`(card) values(floor(1 + (rand()*20)));

#先不建索引的情况下左连接的性能分析,可以看到两表均为全表扫描,并且可能查询的行数均为20条
mysql> explain select * from class left join books on class.card = books.card;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | NULL                                               |
|  1 | SIMPLE      | books | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+

#对于左连接,目前不知道是在左表字段建索引好还是在右表字段建索引好,因此可以先在左表字段建,看看效果,大胆假设,小心求证
alter table class add index idx_class_card(card);

#查看sql的性能分析,可以看到对于添加索引的表type变成了index,其余不变,得到了一定的优化
mysql> explain select * from class left join books on class.card = books.card;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | class | NULL       | index | NULL          | idx_class_card | 4       | NULL |   20 |   100.00 | Using index                                        |
|  1 | SIMPLE      | books | NULL       | ALL   | NULL          | NULL           | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+----------------------------------------------------+

#再来看下在右表建索引的情况
#删除左表索引
drop index idx_class_card on class;
#创建右表索引
create index idx_books_card on books(card);

#查看sql性能分析,可以看到右表的type变成了ref,另外可能查询的行数也明显减少,变成1,因此在左连接中,在右表字段创建索引,性能优化的更多
mysql> explain select * from class left join books on class.card = books.card;
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys  | key            | key_len | ref             | rows | filtered | Extra       |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL           | NULL           | NULL    | NULL            |   20 |   100.00 | NULL        |
|  1 | SIMPLE      | books | NULL       | ref  | idx_books_card | idx_books_card | 4       | test.class.card |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+

#以上现象的原因:这是由左连接的特性决定的,left join条件用于确定如何从右表搜索行,左表的数据一定都有,所以右边是我们的关键点,一定需要建立索引。
#同理,右连接时(right join),该查询条件用于确定如何从左表搜索行,因此左表一定需要建立索引。

三表连接索引优化

三表查询索引优化

#建表
drop table if exists phone;
create table phone(
	phoneId int primary key auto_increment,
	card int not null
);

#插入数据
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));
insert into phone(card) values(floor(rand()*20 + 1));

#三表连接sql分析,三张表的type均为全扫描,根据上面的结果,针对左连接,对右表查询字段建立索引
mysql> explain select * from class left join books on class.card = books.card left join phone on books.card = phone.card;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | NULL                                               |
|  1 | SIMPLE      | books | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | phone | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   20 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+

#建立索引
create index idx_books_card on books(card);
create index idx_phone_card on phone(card);

#sql性能分析,可以看到建立索引的表type变成了ref,使用到了索引,并且行数减小至1行,优化效果较好 ===> 索引最好设置在需要经常查询的字段中
mysql> explain select * from class left join books on class.card = books.card left join phone on books.card = phone.card;
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys  | key            | key_len | ref             | rows | filtered | Extra       |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+
|  1 | SIMPLE      | class | NULL       | ALL  | NULL           | NULL           | NULL    | NULL            |   20 |   100.00 | NULL        |
|  1 | SIMPLE      | books | NULL       | ref  | idx_books_card | idx_books_card | 4       | test.class.card |    1 |   100.00 | Using index |
|  1 | SIMPLE      | phone | NULL       | ref  | idx_phone_card | idx_phone_card | 4       | test.books.card |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+----------------+----------------+---------+-----------------+------+----------+-------------+

总结:

对于join语句的优化,尽可能减少join语句中的NestedLoop的循环总次数:永远用小结果集驱动大的结果集
优先优化NestedLoop的内层循环
保证join语句中被驱动表上join条件字段已经被索引
当无法保证被驱动表的join条件字段被索引且内存资源充足的前提下,不要太吝啬joinBuffer的设置

在以上的所有的名字段中最为重要的有五个:

id type key rows extra

覆盖索引(Covering index)

理解方式一
select的数据列只从索引中就能够取得,不必读取数据行,mysql可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所建索引覆盖
理解方式二
索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此不必读取整个行。毕竟索引叶子节点存储了他们索引的数据;当能通过读取索引就可以获取想要的数据,那就不需要读取行了。一个索引包含(或覆盖了)满足查询结果的数据就叫做覆盖索引。
注意:
如果要使覆盖索引,一定要注意select列表中只取出需要的列,不可select*,因为如果将所有字段一起做索引会导致索引文件过大,查询性能降低。

聚集索引和非聚集索引
聚集索引

非聚集索引

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值