Mysql进阶(中) -- 索引

索引上部分 -> Mysql进阶(上) -- 存储引擎,索引_千帐灯无此声的博客-CSDN博客 

 爸爸妈妈 - 王蓉 - 单曲 - 网易云音乐

目录看左栏

目录

索引

性能分析 - show profiles

性能分析 - explain

使用规则 - 验证索引效率

使用规则 - 最左前缀法则

使用规则 - 索引失效情况1

使用规则 - 索引失效情况2

使用规则 - SQL提示

使用规则 - 覆盖索引&回表查询

使用规则 - 前缀索引

使用规则 - 单列&联合索引

设计原则

小结


索引

性能分析 - show profiles

Mysql进阶(中) -- 索引_第1张图片

Mysql进阶(中) -- 索引_第2张图片

开关未开启

Mysql进阶(中) -- 索引_第3张图片

打开开关

Mysql进阶(中) -- 索引_第4张图片

突然,出现了BUG
Mysql进阶(中) -- 索引_第5张图片

为此我专门写了一篇文章 -> Finalshell连接Linux超时之Connection timed out: connect_千帐灯无此声的博客-CSDN博客

开关打开后,开始查询

Mysql进阶(中) -- 索引_第6张图片

查看每一条SQL语句的耗时

Mysql进阶(中) -- 索引_第7张图片

根据name查询比根据id查询慢很多

查询指定SQL语句

查询指定SQL语句CPU使用情况

Mysql进阶(中) -- 索引_第8张图片

性能分析 - explain

Mysql进阶(中) -- 索引_第9张图片

Datagrip,创建3个表

use itcast;


create table student(
    id   int auto_increment comment '主键ID' primary key,
    name varchar(10) null comment '姓名',
    no   varchar(10) null comment '学号'
)comment '学生表';

INSERT INTO student (name, no) VALUES ('黛绮丝', '2000100101');
INSERT INTO student (name, no) VALUES ('谢逊', '2000100102');
INSERT INTO student (name, no) VALUES ('殷天正', '2000100103');
INSERT INTO student (name, no) VALUES ('韦一笑', '2000100104');



create table course(
    id int auto_increment comment '主键ID' primary key,
    name varchar(10) null comment '课程名称'
)comment '课程表';

INSERT INTO course (name) VALUES ('Java');
INSERT INTO course (name) VALUES ('PHP');
INSERT INTO course (name) VALUES ('MySQL');
INSERT INTO course (name) VALUES ('Hadoop');



create table student_course(
    id int auto_increment comment '主键' primary key,
    studentid int not null comment '学生ID',
    courseid  int not null comment '课程ID',
    constraint fk_courseid foreign key (courseid) references course (id),
    constraint fk_studentid foreign key (studentid) references student (id)
)comment '学生课程中间表';

INSERT INTO student_course (studentid, courseid) VALUES (1, 1);
INSERT INTO student_course (studentid, courseid) VALUES (1, 2);
INSERT INTO student_course (studentid, courseid) VALUES (1, 3);
INSERT INTO student_course (studentid, courseid) VALUES (2, 2);
INSERT INTO student_course (studentid, courseid) VALUES (2, 3);
INSERT INTO student_course (studentid, courseid) VALUES (3, 4);

Datagrip中,右键student_course,打开Diagram可视化

Mysql进阶(中) -- 索引_第10张图片

3张表联查,需要2个where来消除无效笛卡尔积 

查询所有学生选修的课程

查询连接顺序

Mysql进阶(中) -- 索引_第11张图片

查询选修了Mysql的学生

(1)分3步实现

Mysql进阶(中) -- 索引_第12张图片

(2)子查询1步实现

类似的,自己可以再写一个,查询韦一笑选修了什么课程

先逐步拆分

Mysql进阶(中) -- 索引_第13张图片

再子查询  --  书写顺序是:先进入course表,再进入中间表,最后进入student表

(1)course

(2)student_course

(3)student

Mysql进阶(中) -- 索引_第14张图片

再用explain / desc看看执行顺序

Mysql进阶(中) -- 索引_第15张图片

但是执行顺序和书写顺序不同

执行顺序,先执行内层,也就是先student,再course,最后中间表

id越大,越先执行;id相同,按顺序执行

explain各字段含义

Mysql进阶(中) -- 索引_第16张图片

再讲下type

Mysql进阶(中) -- 索引_第17张图片

根据 主键 或 唯一索引 访问,才会出现const比如

Mysql进阶(中) -- 索引_第18张图片

唯一索引

Mysql进阶(中) -- 索引_第19张图片

Mysql进阶(中) -- 索引_第20张图片

所以select时,尽量防止出现all,all代表全表扫描,性能较低

再讲下possible_key

Mysql进阶(中) -- 索引_第21张图片

Mysql进阶(中) -- 索引_第22张图片

Mysql进阶(中) -- 索引_第23张图片

最后讲下

Mysql进阶(中) -- 索引_第24张图片

Mysql进阶(中) -- 索引_第25张图片

需要重点关注的字段

尤其是type,作为性能指标 

使用规则 - 验证索引效率

Mysql进阶(中) -- 索引_第26张图片

Mysql进阶(中) -- 索引_第27张图片

加个  \G ,查询字段以 row 的方式显示 

 执行show index from tb_sku; 后,发现没有 sn 的索引,所以查询1条记录的时间相当于查询 * 的时间,下面我们需要创建 sn 的索引

创建耗时 49s ,因为创建索引,实际上是给 800万条数据,构造B+Tree的过程

再次查询

从 18s 提升到了 0.02s

接着explain查看索引

Mysql进阶(中) -- 索引_第28张图片

sn 这个字段使用了索引 

使用规则 - 最左前缀法则

Mysql进阶(中) -- 索引_第29张图片

不跳过索引中的列

show index可以看到3个索引的顺序

Mysql进阶(中) -- 索引_第30张图片

查询

Mysql进阶(中) -- 索引_第31张图片

去掉结尾 status 再次查询

Mysql进阶(中) -- 索引_第32张图片

最左边的字段 profession 存在,并且没有跳过中间的列

继续查询

Mysql进阶(中) -- 索引_第33张图片

这次跳过 profession(索引最左边的列),索引全部失效

Mysql进阶(中) -- 索引_第34张图片

而且 type = all,全表扫描,因为不满足最左前缀法则

进行个对比

(1)

Mysql进阶(中) -- 索引_第35张图片

(2) 

Mysql进阶(中) -- 索引_第36张图片

索引长度一样,说明(1)没有走 status 的索引,因为不满足最左前缀法则,中间跳过了 age 的索引,所以索引部分失效

possible_keys可能的索引,key实际使用的索引

当顺序改变时,索引长度不变,满足最左前缀法则

Mysql进阶(中) -- 索引_第37张图片

除了 最左前缀法则,还有 范围查询

Mysql进阶(中) -- 索引_第38张图片

(1)由索引长度,status 失效了

Mysql进阶(中) -- 索引_第39张图片

(2)当 > 改成 >= , status生效

Mysql进阶(中) -- 索引_第40张图片

所以,一般业务中,尽量使用 >=, <=,而不是 >

使用规则 - 索引失效情况1

Mysql进阶(中) -- 索引_第41张图片

在索引列上进行 运算,索引列会失效,全表扫描

Mysql进阶(中) -- 索引_第42张图片

substring截取字串,第二个参数,下标从1开始 

 

Mysql进阶(中) -- 索引_第43张图片

可能用到的索引,用了;但实际的 key,没用到,还是全表扫描

Mysql进阶(中) -- 索引_第44张图片

Mysql进阶(中) -- 索引_第45张图片

尾部模糊匹配

Mysql进阶(中) -- 索引_第46张图片头部模糊匹配

Mysql进阶(中) -- 索引_第47张图片

总之前面不能加 % 和 _,大数据查询下,会全表扫描,大数据下性能较低

使用规则 - 索引失效情况2

Mysql进阶(中) -- 索引_第48张图片

创建age索引

Mysql进阶(中) -- 索引_第49张图片

Mysql进阶(中) -- 索引_第50张图片

Mysql进阶(中) -- 索引_第51张图片

Mysql进阶(中) -- 索引_第52张图片

Mysql进阶(中) -- 索引_第53张图片

如果Mysql评估走索引比全表扫描慢,则不走索引

Mysql进阶(中) -- 索引_第54张图片

Mysql进阶(中) -- 索引_第55张图片

当profession置空,结果会反过来

Mysql进阶(中) -- 索引_第56张图片

走不走索引,主要看is null 或 is not null占少数还是多数,少数就会走索引

多数的话,Mysql默认不如全表扫描,会直接全表扫描,所以不走索引

使用规则 - SQL提示

注意,为了恢复profession,不是删表再重新插入,而是清空再insert,否则之前创建的联合索引等,还得重新创建

create index idx_user_name on tb_user(name);
 
create unique index idx_user_phone on tb_user(phone);
 
create index idx_user_pro_age_sta on tb_user(profession,age,status);
 
create index idx_user_age on tb_user(age);

create index idx_user_email on tb_user(email);

给profession创建单列索引

Mysql进阶(中) -- 索引_第57张图片

一个单列,一个联合索引,Mysql选择了联合索引

Mysql进阶(中) -- 索引_第58张图片

提示Mysql到底用哪个索引

Mysql进阶(中) -- 索引_第59张图片

use用,ignore不用,force必须用,跟在表名后

使用规则 - 覆盖索引&回表查询

Mysql进阶(中) -- 索引_第60张图片

一般using index condition对应select *

而using where; using index对应具体的select

(1)

(2)

(1)需要回表查询,效率较低,(2)不需要

因为pro, age, status的联合索引,属于二级索引,二级索引叶子节点挂的是id,此时(2)中直接查询二级索引,就得到了需要的数据

但是(1)中name字段,不在这个二级索引中,还要到id的聚集索引中查找

Mysql进阶(中) -- 索引_第61张图片关于聚集索引,二级索引和回表查询,可以看文章中,索引-分类这一部分

Mysql进阶(上) -- 存储引擎,索引_千帐灯无此声的博客-CSDN博客

Mysql进阶(中) -- 索引_第62张图片

覆盖索引,通俗点讲,就是只需要查询一次,通过索引本身可以满足查询需求。

覆盖索引不需要回表查询。

至于上面提到的,覆盖索引时,避免使用 select *,是因为,容易发生回表查询,除非创建联合索引,而查询的列正好是联合索引包含的列和id

一道面试题

Mysql进阶(中) -- 索引_第63张图片

如何简历索引,建立怎样的索引,才能是最优方案呢

答:根据username, password两个字段,建立联合索引(二级索引),而二级索引叶子下挂的就是id,即覆盖索引,不需要回表查询

使用规则 - 前缀索引

Mysql进阶(中) -- 索引_第64张图片

例如,计算email的选择性

Mysql进阶(中) -- 索引_第65张图片

记录总数24

Mysql进阶(中) -- 索引_第66张图片

不重复的email也是24

Mysql进阶(中) -- 索引_第67张图片

所以email的选择性 = 24 / 24 = 1

Mysql进阶(中) -- 索引_第68张图片

观察前缀索引,先截取前10个字符,节约索引空间

Mysql进阶(中) -- 索引_第69张图片

截取前9个

Mysql进阶(中) -- 索引_第70张图片

直到.....

Mysql进阶(中) -- 索引_第71张图片

截取到前5个,依然是9583

针对email建立前缀索引

Mysql进阶(中) -- 索引_第72张图片

两个5表示,对email取5个前缀

Mysql进阶(中) -- 索引_第73张图片Sub_part表示截取字段

前缀索引使用

Mysql进阶(中) -- 索引_第74张图片

Mysql进阶(中) -- 索引_第75张图片

大文本或长字符串,采取前缀索引,降低索引体积,提高查询效率,避免对磁盘IO的浪费

前缀索引查询流程

Mysql进阶(中) -- 索引_第76张图片

首先,主键id会创建聚集索引,我们再创建前缀索引

截取前5个字符是因为,区分度已经足够高

Mysql进阶(中) -- 索引_第77张图片

逐个匹配,比如 lvbu6,l比d大,所以往右走,来到lvbu6.........

到了叶节点,拿到对应数据后,我们还得对整个email进行对比.....

使用规则 - 单列&联合索引

补充:键盘右上角,Home键定位命令行头,Ended键定位尾部。

Mysql进阶(中) -- 索引_第78张图片

下面解释为什么,多个联合条件时,推荐使用,联合索引

创建联合索引

Mysql进阶(中) -- 索引_第79张图片

执行下列语句

extra为Null,表示回表查询了因为单列索引和联合索引都存在时,默认单列

Mysql进阶(中) -- 索引_第80张图片

当我们建议Mysql使用联合索引

Using index表示覆盖索引,不需要回表查询

联合索引情况

注意,创建联合索引时,需要考虑字段顺序,根据最左前缀法则,最左边的列,必须非空,否则索引会失效

设计原则

Mysql进阶(中) -- 索引_第81张图片

(1)比如,100万条数据,就需要建立索引,而几千几万条数据是不需要的

(3)区分度指的是,比如身份证,姓名,部门,区分度最高的是身份证

(4)大文本,长字符串,需要建立前缀索引(也需要考虑区分度)

(5)尽量使用联合索引,可以覆盖索引,避免回表

(6)只建立有必要的索引,降低维护成本

小结

Mysql进阶(中) -- 索引_第82张图片--Mysql进阶(中) -- 索引_第83张图片

你可能感兴趣的:(Mysql,sql,mysql,linux,数据库)