MySQL 之 索引

一、为何要有索引

​ 对查询语句的优化,加速查询mysql

二、什么是索引

​ 索引在MySQL中也叫是一种‘键’,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能很是关键,尤为是当表中的数据量愈来愈大时,索引对于性能的影响愈发重要。sql

​ 索引优化应该是对查询性能优化最有效的手段了。索引可以轻易将查询性能提升好几个数量级。数据库

​ 索引至关于字典的音序表,若是要查某个字,若是不使用音序表,则须要从几百页中逐页去查。性能优化

三、索引的原理

(1)、 索引原理

​ 本质都是:经过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,就能够老是用同一种查找方式来锁定数据。数据结构

(2)、 磁盘IO与预读

​ 磁盘读取数据靠的是机械运动,每次读取数据花费的时间能够分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所须要的时间,主流磁盘通常在5ms如下;旋转延迟就是磁盘转速,好比一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2= 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,通常在零点几毫秒,相对于前两个时间能够忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17= 9ms左右。但一台500 -MIPS的机器每秒能够执行5亿条指令,由于指令依靠的是电的性质,换句话说执行一次IO的时间能够执行约450万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。函数

​ 考虑到磁盘IO是很是高昂的操做,计算机操做系统作了一些优化,当一次IO时,不光把当前磁盘地址的数据,还把相邻的数据也都读取到内存缓冲区内,由于由局部预读性原理可知,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据称之为一页(page)。具体一页有多大数据跟操做系统有关,通常为4k或8k,也就是读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计很是有帮助。性能

四、索引的数据结构

(1)、树

​ 树状图是一种数据结构,它是由n(n>=1)个有限结点组成一个具备层次关系的集合。把它叫作“树”是由于它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。mysql索引

​ 它具备如下的特色:每一个节点有零个或多个子节点;没有父节点的节点称为根节点;每个非根节点有且只有一个父节点;除了根节点外,每一个子节点能够分为多个不相交的子树。测试

(2)、B树

​ 平衡树 balance tree - B树大数据

(3)、B+树

​ B+树是经过二叉查找树,再由平衡二叉树,B树演化而来, 是为了更好的处理范围问题在b树的基础上有所优化。mysql 中innodb存储引擎的全部的索引树都是b+树

五、汇集索引与辅助索引

​ 在数据库中,B+树的高度通常都在2~4层,这也就是说查找某一个键值的行记录时最多只须要2到4次IO,当前通常的机械硬盘每秒至少能够作100次IO,2~4次的IO意味着查询时间只须要0.02~0.04秒。

​ 数据库中的B+树索引能够分为汇集索引(clustered index)和辅助索引(secondary index)

(1)、汇集索引与辅助索引的相同点:

​ 汇集索引与辅助索引相同的是:无论是汇集索引仍是辅助索引,其内部都是B+树的形式,即高度是平衡的,叶子结点存放着全部的数据。

(2)、汇集索引与辅助索引的不相同点:

​ 汇集索引与辅助索引不一样的是:叶子结点存放的是不是一整行的信息。

<1>、 汇集索引/聚簇索引:叶子节点会存储整行数据 ----- innodb 的主键
# InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。
而汇集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,也将汇集索引的叶子结点称为数据页。
汇集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构同样,每一个数据页都经过一个双向链表来进行连接。
    
# 若是未定义主键,MySQL取第一个惟一索引(unique)并且只含非空列(NOT NULL)做为主键,InnoDB使用它做为聚簇索引。   
# 若是没有这样的列,InnoDB就本身产生一个这样的ID值,它有六个字节,并且是隐藏的,使其做为聚簇索引。

# 因为实际的数据页只能按照一棵B+树进行排序,所以每张表只能拥有一个汇集索引。
在多数状况下,查询优化器倾向于采用汇集索引。由于汇集索引可以在B+树索引的叶子节点上直接找到数据。
此外因为定义了数据的逻辑顺序,汇集索引可以特别快地访问针对范围值得查询。
<2>、 辅助索引/非汇集索引:除了主键以外的普通索引都是辅助索引,一个索引没办法查到整行数据,须要回汇集索引再查一次(回表)
表中除了汇集索引外其余索引都是辅助索引(Secondary Index,也称为非汇集索引),与汇集索引的区别是:辅助索引的叶子节点不包含行记录的所有数据。
叶子节点除了包含键值之外,每一个叶子节点中的索引行中还包含一个书签(bookmark)。该书签用来告诉InnoDB存储引擎去哪里能够找到与索引相对应的行数据。
因为InnoDB存储引擎是索引组织表,所以InnoDB存储引擎的辅助索引的书签就是相应行数据的汇集索引键。
辅助索引的存在并不影响数据在汇集索引中的组织,所以每张表上能够有多个辅助索引,但只能有一个汇集索引。当经过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并经过叶子级别的指针得到只想主键索引的主键,而后再经过主键索引来找到一个完整的行记录。
<3>、聚焦索引和非聚焦索引的区别
# 汇集索引
1.纪录的索引顺序与物理顺序相同
   所以更适合between and和order by操做
2.叶子结点直接对应数据
 从中间级的索引页的索引行直接对应数据页
3.每张表只能建立一个汇集索引

# 非汇集索引
1.索引顺序和物理顺序无关
2.叶子结点不直接指向数据页
3.每张表能够有多个非汇集索引,须要更多磁盘和内容
   多个索引会影响insert和update的速度

六、MySQL索引管理

(1)、索引功能

1. 索引的功能就是加速查找
2. mysql中的primary key,unique,联合惟一也都是索引,这些索引除了加速查找之外,还有约束的功能

(2)、MySQL经常使用的索引

普通索引 INDEX:加速查找

惟一索引:
    -主键索引 PRIMARY KEY:加速查找+约束(不为空、不能重复)
    -惟一索引 UNIQUE:加速查找+约束(不能重复)

联合索引:
    -PRIMARY KEY(id,name):联合主键索引
    -UNIQUE(id,name):联合惟一索引
    -INDEX(id,name):联合普通索引

(3)、各个索引的应用场景

# 举个例子来讲,好比你在为某商场作一个会员卡的系统。这个系统有一个会员表,有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(11)
会员住址 VARCHAR(50)
会员备注信息 TEXT

那么这个 会员编号,做为主键,使用 PRIMARY
会员姓名 若是要建索引的话,那么就是普通的 INDEX
会员身份证号码 若是要建索引的话,那么能够选择 UNIQUE (惟一的,不容许重复)

# 除此以外还有全文索引,即 FULLTEXT
会员备注信息 , 若是须要建索引的话,能够选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,若是就一两行字的,普通的 INDEX 也能够。
但其实对于全文搜索,咱们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来作全文搜索。

# 其余的如空间索引SPATIAL,了解便可,几乎不用

(4)、索引的两大类型 hash 与 btree

# 咱们能够在建立上述索引的时候,为其指定索引类型,分两类
hash 类型的索引:查询单条快,范围查询慢
btree 类型的索引:b+树,层数越多,数据量指数级增加(咱们就用它,由于innodb默认支持它)

#不 同的存储引擎支持的索引类型也不同
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

(5)、操做索引: 建立和删除

<1> 建立:create index 索引名 on 表名(字段名);
create index  id on s1(id);
alter table s1 add index ix_sex(sex);

<2> 删除: drop index 索引名 on 表名;

drop index  id on 表名;

七、测试索引

(1)、准备数据

# 1. 准备表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);

# 2. 建立存储过程,实现批量插入记录
delimiter $$ #声明存储过程的结束符号为$$
create procedure auto_insert1()
BEGIN
    declare i int default 1;
    while(i<30000000)do
        insert into s1 values(i,'cai','male',concat('cai',i,'@yong'));
        set i=i+1;
    end while;
END$$ #$$结束
delimiter ; #从新声明分号为结束符号

# 3. 查看存储过程
show create procedure auto_insert1\G 

# 4. 调用存储过程
call auto_insert1();

(2)、 在没有索引的前提下测试查询速度

# 无索引:mysql根本就不知道究竟是否存在id等于333333333的记录,只能把数据表从头至尾扫描一遍,此时有多少个磁盘块就须要进行多少IO操做,因此查询速度很慢
mysql> select * from s1 where id=333333333;
Empty set (0.33 sec)

(3)、 在表中已经存在大量数据的前提下,为某个字段段创建索引,创建速度会很慢

img

(4)、 在索引创建完毕后,以该字段为查询条件时,查询速度提高明显

img

注意:

  • mysql先去索引表里根据b+树的搜索原理很快搜索到id等于333333333的记录不存在,IO大大下降,于是速度明显提高
  • 能够去mysql的data目录下找到该表,能够看到占用的硬盘空间多大
  • 须要注意,以下图

img

(5)、总结

# 1.必定是为搜索条件的字段建立索引,好比select * from s1 where id = 333;就须要为id加上索引
# 2.在表中已经有大量数据的状况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快
好比create index idx on s1(id);会扫描表中全部的数据,而后以id为数据项,建立索引结构,存放于硬盘的表中。
建完之后,再查询就会很快了。
#3. 须要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI
MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在innodb中,表数据文件自己就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以innodb表数据文件自己就是主索引。
由于inndob的数据文件要按照主键汇集,因此innodb要求表必需要有主键(Myisam能够没有),若是没有显式定义,则mysql系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则mysql会自动为innodb表生成一个隐含字段做为主键,这字段的长度为6个字节,类型为长整型.

八、正确使用索引

(1).只有对建立了索引的列进行条件筛选的时候效率才会高

(2).索引对应的列作条件不能参与运算、不能使用函数

(3).当某一列的区分度很是小(重复率高),不适合建立索引

(4).当范围做为条件的时候,查询结果的范围越大越慢,越小越快

(5).like关键字:若是使用%/ 开头都没法命中索引

(6).多个条件:若是只有一部分建立了索引,条件用and相连,那么能够提升查询效率。(若是用or相连,不能提升查询效率)

and
   select count(*) from s1 where id=1000000  and email = 'eva1000000@oldboy';   # 查询速度加快
or
   select count(*) from s1 where id=1000000  or email = 'eva1000000@oldboy';

(7).联合索引:联合索引是指对表上的多个列合起来作一个索引。联合索引的建立方法与单个索引的建立方法同样,不一样之处仅在于有多个索引列。

creat index ind_mix on s1(id,name,email);
select count(*) from s1 where id=1000000  and email = 'eva1000000@oldboy';  # 快
select count(*) from s1 where id=1000000  or email = 'eva1000000@oldboy';   # 慢   条件不能用or
select count(*) from s1 where id=1000000;                                   # 快
select count(*) from s1 where email = 'eva1000000@oldboy';                  # 慢   要服从最左前缀原则
select count(*) from s1 where id>1000000  and email = 'eva1000000@oldboy';  # 慢   从使用了范围的条件开始以后的索引都失效
相关文章
相关标签/搜索