索引
索引
1、索引概述
知识回顾:数据都是存在硬盘上的,那查询数据不可避免的需要进行IO操作
索引就是一种数据结构,类似于书的目录。意味着以后再查数据应该先找目录再找数据,而不是用翻页的方式查询数据
2、键
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。
- primary key
- unique key
- index key
1.上述的三个key都可以加快数据查询
2.primary key和unique key除了可以加快查询本身还自带限制条件(primary key:非空且唯一,unique key:唯一),而index key很单一就是用来加快数据查询
3.外键不属于索引键的范围,是用来建立关系的,与加快查询无关
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
索引加快查询的本质
id int primary key auto_increment,
name varchar(32) unique,
province varchar(32)
age int
phone bigint
select name from userinfo where phone=18818888888; # 一页页的翻
select name from userinfo where id=99999; # 按照目录确定页数找
索引可以加快数据查询,但是会降低增删的速度。
通常情况下我们频繁使用某些字段查询数据
为了提升查询的速度可以将该字段建立索引,其余字段不要加索引。
索引的影响:
-
在表中有大量数据的前提下,创建索引速度会很慢
以后实际添加索引的时候,尽量在空表的时候添加,在创建表的时候就添加索引,此时添加索引是最快的。 如果表中数据已经有了,还需要添加索引,也可以,只不过创建索引的速度会很慢,不建议这样做
-
在索引创建完毕后,对表的查询性能会大幅度提升,但是写的性能会降低
但是,写的性能影响不是很大,因为在实际中,写的频率很少,大部分操作都是查询 # 如何添加索引?到底给哪些字段加索引呢? '''没有固定答案,具体给哪个字段加索引,要看你实际的查询条件''' select * from user where name='' and password=''; # 索引的使用其实是需要大量的工作经验,才能正确的判断出 '''不要一创建表就加索引,在一张表中,最多最多不要超过15个索引,索引越多,性能就会下降''' # 如何数据量比较小,不需要加索引,100w一下一般不用加,mysql针对于1000w一下的数据,性能不会下降太多.
3、聚集索引和辅助索引
聚集索引(primary key)
聚集索引:主键、主键索引。其实指的就是表的主键,innodb引擎规定一张表中必须要有主键。先来回顾一下存储引擎。
myisam在建表的时候对应到硬盘有几个文件(三个)?
innodb在建表的时候对应到硬盘有几个文件(两个)?frm文件只存放表结构,不可能放索引,也就意味着innodb的索引跟数据都放在idb表数据文件中。
特点:叶子结点放的一条条完整的记录
辅助索引(unique,index)
辅助索引:除主键以外的都是辅助索引。查询数据的时候不可能都是用id作为筛选条件,也可能会用name,password等字段信息,那么这个时候就无法利用到聚集索引的加速查询效果。就需要给其他字段建立索引,这些索引就叫辅助索引
特点:叶子结点存放的是辅助索引字段对应的那条记录的主键的值(比如:按照name字段创建索引,那么叶子节点存放的是:{name对应的值:name所在的那条记录的主键值})
select name from user where name='jack';
上述语句叫覆盖索引:所要的数据跟你的条件是一个字段。只在辅助索引的叶子节点中就已经找到了所有我们想要的数据。
select age from user where name='jack';
上述语句叫非覆盖索引,条件字段跟你想要查找的字段不一样。虽然查询的时候命中了索引字段name,但是要查的是age字段,所以还需要利用主键才去查找。
4、索引数据结构
索引底层其实是树结构:
树是计算机底层的数据结构,树中的每一个节点存储的数据大小是固定的。树结构越低越好。
树有很多中类型
二叉树、b树、b+树、B*树......
节点有:根节点(第一个)、枝节点、叶子节点(不分叉的末尾)
# 二叉树
二叉树里面还可以细分成很多领域,我们简单的了解即可
二叉意味着每个节点最多只能分两个子节点
# B树
所有的节点都可以存放完整的数据
# B+\*树
只有叶子节点才会存放真正的数据,其他节点只存放索引数据
B+:叶子节点增加了指向其他叶子节点的指针
B*:叶子节点和枝节点都有指向其他节点的指针
查询次数由树的层级决定,层级越低次数越少
一个磁盘块儿的大小是一定的,那也就意味着能存的数据量是一定的。如何保证树的层级最低呢?一个磁盘块儿存放占用空间比较小的数据项
# 以后加索引的时候,尽量给字段中存的是数字的列加,我们使用主键查询速度很快
select * from user where name = ''
select * from user where id = '' # 主键查询的更快一些
思考我们应该给我们一张表里面的什么字段字段建立索引能够降低树的层级高度>>> 主键id字段
辅助索引在查询数据的时候最会还是需要借助于聚集索引
辅助索引:叶子节点存放的是数据的主键值。
查询步骤:先根据辅助索引查找到主键值,再根据聚集索引查找真实的数据。
# 有时候就算采用索引字段查询数据 也可能不会走索引!!!
1.没有查询条件,或者查询条件没有建立索引
2.查询结果集是原表中的大部分数据,应该是25%以上
3.索引本身失效,统计数据不真实
4.查询条件使用函数再索引列上或者对索引列进行运算(包括+,-,*等)
5.<,>,in,not in不走索引
6.like'%_'百分号在最前面不走索引
7.单独引用联合索引里非第一位置的索引列
最好能记三个左右的特殊情况'''面试用'''
5、测试索引
准备数据:
#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<3000000)do
insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$结束
delimiter ; #重新声明分号为结束符号
#3. 查看存储过程
show create procedure auto_insert1\G
#4. 调用存储过程
call auto_insert1();
# 表没有任何索引的情况下
select * from s1 where id=30000;
# 避免打印带来的时间损耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;
# 给id做一个主键
alter table s1 add primary key(id); # 速度很慢
select count(id) from s1 where id = 1; # 速度相较于未建索引之前两者差着数量级
select count(id) from s1 where name = 'jason' # 速度仍然很慢
"""
范围问题
"""
# 并不是加了索引,以后查询的时候按照这个字段速度就一定快
select count(id) from s1 where id > 1; # 速度相较于id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;
alter table s1 drop primary key; # 删除主键 单独再来研究name字段
select count(id) from s1 where name = 'jason'; # 又慢了
create index idx_name on s1(name); # 给s1表的name字段创建索引
select count(id) from s1 where name = 'jason' # 仍然很慢!!!
"""
再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分
那这个树其实就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';
# 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性
# 区分度低的字段不能建索引
drop index idx_name on s1;
# 给id字段建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3; # 快了
select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要参与计算
drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 并没有加速
drop index idx_name on s1;
# 给name,gender这种区分度不高的字段加上索引并不难加快查询速度
create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通过id已经讲数据快速锁定成了一条了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基于id查出来的数据仍然很多,然后还要去比较其他字段
drop index idx_id on s1
create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通过email字段一剑封喉
6、联合索引
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';
# 如果上述四个字段区分度都很高,那给谁建都能加速查询
# 给email加然而不用email字段
select count(id) from s1 where name='jason' and gender = 'male' and id > 3;
# 给name加然而不用name字段
select count(id) from s1 where gender = 'male' and id > 3;
# 给gender加然而不用gender字段
select count(id) from s1 where id > 3;
# 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间
create index idx_all on s1(email,name,gender,id); # 最左匹配原则,区分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度变快
7、慢查询日志
设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!
# 公司中一般会使用云数据库------->购买的阿里云---------->阿里云的rds数据库
# 我们自己在公司中需要自己搭建一个本地的测试数据库