Mysql进阶(中) -- 索引

本文详细探讨了MySQL数据库的索引管理,包括性能分析工具、使用规则(如验证索引效率、最左前缀法则、索引失效情况、SQL提示和覆盖索引等)、设计原则以及常见场景的最佳实践。

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

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

👈目录看左栏

目录

🌼索引

🐻性能分析 - show profiles

🐻性能分析 - explain

🍌使用规则 - 验证索引效率

🍌使用规则 - 最左前缀法则

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

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

🍌使用规则 - SQL提示

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

🍌使用规则 - 前缀索引

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

🍉设计原则

🍉小结


🌼索引

🐻性能分析 - show profiles

开关未开启👇

打开开关👇

突然,出现了BUG👇

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

开关打开后,开始查询👇

查看每一条SQL语句的耗时👇

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

查询指定SQL语句👇

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

🐻性能分析 - explain

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可视化👇

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

查询所有学生选修的课程👇

查询连接顺序👇

查询选修了Mysql的学生👇

(1)分3步实现

(2)子查询1步实现👇

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

先逐步拆分👇

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

(1)course

(2)student_course

(3)student

再用explain / desc看看执行顺序👇

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

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

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

explain各字段含义👇

再讲下type👇

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

唯一索引👇

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

再讲下possible_key

最后讲下👇

需要重点关注的字段👇

尤其是type,作为性能指标 

🍌使用规则 - 验证索引效率

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

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

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

再次查询👇

从 18s 提升到了 0.02s

接着explain查看索引👇

sn 这个字段使用了索引 

🍌使用规则 - 最左前缀法则

不跳过索引中的列👆

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

查询👇

去掉结尾 status 再次查询👇

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

继续查询

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

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

进行个对比

(1)

(2) 

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

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

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

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

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

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

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

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

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

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

 

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

尾部模糊匹配

头部模糊匹配

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

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

创建age索引👇

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

当profession置空,结果会反过来👇

走不走索引,主要看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选择了联合索引👇

提示Mysql到底用哪个索引👇

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

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

一般using index condition对应select *

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

(1)

(2)

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

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

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

关于聚集索引,二级索引和回表查询,可以看👇文章中,索引-分类这一部分

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

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

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

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

一道面试题👇

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

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

🍌使用规则 - 前缀索引

例如,计算email的选择性👇

记录总数24👇

不重复的email也是24👇

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

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

截取前9个👇

直到.....👇

截取到前5个,依然是9583

针对email建立前缀索引

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

Sub_part表示截取字段

前缀索引使用👇

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

前缀索引查询流程

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

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

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

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

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

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

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

创建联合索引

执行下列语句

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

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

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

联合索引情况👇

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

🍉设计原则

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

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

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

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

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

🍉小结

--

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千帐灯无此声

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值