MySQL 中,通常有两种方式访问数据库表的行数据:顺序访问和索引访问
ALTER TABLE `table_name` ADD INDEX 索引名 ( 字段名 );
ALTER TABLE user_info ADD INDEX index_login_phone_number ( login_phone_number );
创建和维护索引组要耗费时间,并且随着数据量的增加所耗费的时间也会增加。
索引需要占用额外的磁盘空间,除了数据表占数据空间以外,每一个索引还要占一定的物理空间。如果有大量的索引,索引文件可能比数据文件更快达到最大文件尺寸。
当对表中的数据进行增加、删除和修改的时候,索引也要动态维护,这样就降低了增删改的速度。
索引的类型和存储引擎有关,每种存储引擎所支持的索引类型不一定完全相同。MySQL 中的索引,可以从存储方式、使用逻辑和实际使用等不同角度来进行分类
I/O 消耗,相对于内存存取的消耗,I/O 存取的消耗要更高。所以评价索引的优劣最重要的指标,就是在查找过程中磁盘 I/O 操作次数的复杂度,而索引的本质都是基于某种数据结构来设计的,所以,索引的数据结构要尽量减少查找过程中磁盘 I/O 的存取次数MySQL 中常用的索引,在物理上分为 B+Tree 索引 和 HASH 索引两类,两种不同类型的索引各有其不同的适用范围B+Tree 是BTree 的一种特殊变种

假设要从图中查找id = X的数据,BTREE 搜索过程如下:
40和60两个关键字。X = 40,则命中;如果X < 40走P1;如果40 < X < 60走P2;如果X = 60,则命中;如果X > 60走P3。B+Tree 在原有 BTree 的基础上补充了如下特性:
B+Tree 根节点和支节点没有数据区,数据data全部存储在叶子节点中;
假设为字段ID添加索引,搜索X = 1的数据,**B+TREE** 搜索过程如下:
1,28,66三个关键字。X <= 1 走P1,取出磁盘块,加载1,10,20三个关键字。X <= 1 走P1,取出磁盘块,加载1, 8, 9三个关键字。1,加载对应数据节点BTree的数据(或指向数据的指针)存在每个节点里,而 B+Tree的数据(或指向数据的指针)仅存在叶子节点里,非叶子节点只有索引。BTree 的查找,可能会在任意一个节点停止,而 B+Tree的查找相对稳定。B+Tree 的非叶子节点可以存储更多的索引值,阶数更高B+Tree 的叶子节点使用双向链表链接,提高顺序查询效率BTree , B+Tree 在区间查找方面更胜一筹B+Tree**全表扫描能力更强。**如果我们要根据索引去进行数据表的扫描,如果基于BTREE进行扫描,需要把整棵树遍历一遍,而B+TREE只需要遍历所有叶子节点即可(叶子节点之间形成有序列表)。B+Tree排序能力更强B+TREE**磁盘读写能力更强。**他的根节点和枝节点不保存数据区,所以根节点和枝节点同样大小的情况下,保存的关键字要比BTREE要多。所以,B+TREE读写一次磁盘加载的关键字比BTREE更多。B+Tree查询性能稳定。B+Tree数据只保存在叶子节点,每次查询数据,查询IO次数一定是稳定的HASH 索引。MySQL 目前仅有 MEMORY存储引擎和 HEAP存储引擎支持这类索引。key)通过哈希算法,计算该字段的哈希值,用于访问哈希表结构中的对应索引,该索引指向数据行
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的优点,比如:快速的精准查找根据索引的具体用途,MySQL 中的索引在使用逻辑上分为以下 4 类:
MySQL 中最基本的索引类型,它没有任何限制,唯一任务就是加快系统对数据的访问速度。示例:
在 tb_student 表中的 id 字段上建立名为 index_id 的索引
CREATE INDEX index_id ON tb_student(id);
示例:
在 tb_student 表中的 id 字段上建立名为 index_id 的索引
CREATE UNIQUE INDEX index_id ON tb_student(id);
PRIMARY KEY 关键字。不能使用 CREATE ``INDEX 语句创建主键索引,需要以创建或修改表结构的方式进行添加示例:
在 tb_student 表中的 id 字段上添加主键索引
ALTER TABLE tb_student ADD PRIMARY KEY (id)
CHAR、VARCHAR 或 TEXT 类型的列上创建。全文索引允许在索引列中插入重复值和空值。示例:
在 tb_student 表中的 info 字段上建立名为 index_info 的全文索引
CREATE FULLTEXT INDEX index_info ON tb_student(info);
示例:
在 tb_student 表中的 name 和 address 字段上建立名为 index_na 的索引。该索引创建好了以后,查询条件中必须有 name 字段才能使用索引
CREATE INDEX index_na ON tb_student(name,address);
(c1,c2,c3),在实际查询中,系统用来实际加速的索引有三个:单个索引(c1)、双列索引(c1,c2)和多列索引(c1,c2,c3)MYD文件:数据文件,所有的数据保存在这个文件中。MYI文件 :索引文件
id = 40的数据:MyISAM索引文件(user_myisam.MYI)去找id = 40的节点,通过这个节点的数据区拿到真正保存数据的磁盘地址,再通过这个地址从MYD数据文件(user_myisam.MYD)中加载对应的记录
如果有多个索引,在MyISAM存储引擎中,主键索引 和 **普通索引(辅助索引)**是同级别的,没有主次之分

Innodb主键索引是一个聚集索引。
如果有多个索引,在Innodb存储引擎中,主键索引的叶子节点保存的是真正的数据。而辅助索引叶子节点的数据区保存的是主键索引关键字的值。
假如要查询name = C 的数据,其搜索过程如下:
C查询最后找到主键id = 9.id=9的数据,最终在主键索引的叶子节点中获取到真正的数据。

索引可以提高查询的速度,但并不是使用带有索引的字段查询时,索引都会起作用。使用索引有几种特殊情况,在这些情况下,有可能使用带有索引的字段查询时,索引并没有起作用,下面重点介绍这几种特殊情况
%”,索引不会被使用。如果“%”不是在第一个位置,索引就会被命中使用。north_american_box_office表中的zhongwenpianming字段添加索引index_zhongwenpianmingCREATE INDEX index_zhongwenpianming ON north_american_box_office(zhongwenpianming);
通过使用EXPLAIN语句,观察SQL执行计划,检查索引是否生效
在where条件中,使用=进行查询,索引正常被使用
--索引生效
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming = '狮子王';

在where条件中,使用like进行查询,如果匹配字符串的第一个字符为通配符“%”,索引不会被使用
--索引失效
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming LIKE '%狮%';
或者
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming LIKE '%狮';

在where条件中,使用like进行查询,如果匹配字符串的第一个字符为正常字符内容,索引正常被使用
--索引有效
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming LIKE '狮%';

north_american_box_office表中的shangyingtianshu字段和leijipiaofang字段添加多列索引index_tianshu_piaofang--创建索引
CREATE INDEX index_tianshu_piaofang ON north_american_box_office(shangyingtianshu, leijipiaofang);
--索引有效
EXPLAIN SELECT * FROM north_american_box_office
WHERE (shangyingtianshu BETWEEN 9 AND 13) AND leijipiaofang >=3000

--索引失效
EXPLAIN SELECT * FROM north_american_box_office
WHERE leijipiaofang >=3000

--索引失效
EXPLAIN SELECT * FROM north_american_box_office
WHERE shangyingtianshu >= 9 AND leijipiaofang >=3000

MySQL在优化器阶段,发现全表扫描比走索引效率更高,因此就放弃使用索引。当MySQL发现通过索引扫描的行记录数超过全表的10%-30%时,优化器可能会放弃走索引,自动变成全表扫描。>、< 、>=、<=、in等条件)时往往会出现上述情况OR关键字查询语句只有 OR 关键字时,如果 OR 前后的两个条件的列都是索引,那么查询中将使用索引。如果 OR 前后有一个条件的列不是索引,那么查询中将不使用索引
--索引有效
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming LIKE '狮%' OR shangyingtianshu BETWEEN 90 AND 100

--索引失效
EXPLAIN SELECT * FROM north_american_box_office
WHERE zhongwenpianming LIKE '狮%' OR leijipiaofang BETWEEN 90 AND 100

虽然OR关键字前的第一个条件中的字段zhongwenpianming虽然存在索引,但是由于多列索引index_tianshu_piaofang并不包含leijipiaofang字段,导致OR关键字后面的条件不存在索引,所以导致整条SQL语句的索引失效
查询语句中使用函数时,会造成查询中将不使用索引
--索引失效
EXPLAIN SELECT * FROM north_american_box_office
WHERE length(zhongwenpianming) = 9

索引的选择,可以遵循一些已有的原则,创建索引的时候应尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引
count(distinct column_name):count(*),即:去重后的列值个数 VS 总个数。经常需要 ORDER BY、GROUP BY、DISTINCT 和 UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作/3、
如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度
MySQL 在生成一个执行计划时,要考虑各个索引,这也要花费时间。创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL 选择不到所要使用的最佳索引。(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。例如:(name,city)和(name)这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者的如果索引的值很长,那么查询的速度会受到影响。例如:对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多
由于数据较小,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果
如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和 BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度
表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。应该定期找出这些索引,将它们删除,从而减少索引对更新操作的影响
如果查询的列,通过索引项的信息可直接返回,则该索引称为查询SQL的覆盖索引。覆盖索引可以提高查询的效率。
例如:通过name进行数据查询
select * from users where name = ?
需要需要在name索引中找到name对应的id,然后通过获取的id在主键索引中查到对应的行。整个过程需要扫描两次索引,一次name,一次id

如果我们查询只想查询id的值,就可以改写SQL为
select id from users where name = ?
id的值,通过name查询的时候,扫描完name索引,我们就能够获得id的值了,所以就不需要再去扫描id索引,就会直接返回。此时,该索引就是当前SQL的覆盖索引。age的值select id,age from users where name = ?
这样就无法使用到覆盖索引了。所以,这也是为什么SQL中要求尽量不要使用select *,要写明具体要查询的字段。其中一个原因就是在可以使用到覆盖索引的情况下,不需要进入到数据区,数据就能直接返回,提升了查询效率