最全如何创建高性能的索引,Java程序员最大的悲哀是什么

最后

光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性

Java面试宝典2021版

最常见Java面试题解析(2021最新版)

2021企业Java面试题精选

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

从优到劣依次为: system > const > eq_ref > ref > range > index > all

索引优化一般需要达到最少range级别

blog.csdn.net/weixin_4434…

possible_keys:表示查询时,可能使用的索引

表示此次查询可以用到的索引,如果没有相关的索引,此列是NULL

key:表示实际使用的索引

如果此次查询没有用到索引,此列是NULL

key_len:索引字段的长度

索引使用到的字节数,该列显示的为索引字段的最大可能长度,并非世纪使用的长度,在定义索引是计算出来的,并非实时计算出来.

长度计算公式:

varchr(10)变长字段且允许NULL = 10 * ( character set:uff8mb4=4, utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)

varchr(10)变长字段且不允许NULL = 10 *( character set: uff8mb4=4,utf8=3,gbk=2,latin1=1)+2(变长字段)

char(10)固定字段且允许NULL = 10 * ( character set:uff8mb4=4,utf8=3,gbk=2,latin1=1)+1(NULL)

char(10)固定字段且不允许NULL = 10 * ( character set:uff8mb4=4,utf8=3,gbk=2,latin1=1)

ref: 引用到上一个表的列

rows:扫描出的行数(估算的行数)

filtered:按表条件过滤的行百分比

Extra:额外的信息说明

这里挑比较常见的几个做下说明:

Using index: 使用到了覆盖索引

Using where:

1.查询的列未被索引覆盖,where子句筛选条件不满足前缀索引(索引失效)

2.查询的列未被索引覆盖,where子句筛选条件非索引列

Using index condition: 范围过滤索引

Using filesort: 使用到了磁盘排序,这是应该避免的

Using temporary : 使用到了临时表,这是应该避免的

索引是什么?


索引是帮助查询快速找到记录的一种高效的查询数据结构.

索引是针对查询优化最有效的手段.索引能够将查询性能提高好几个数量级.

索引是如何工作的?


在Mysql中,首先存储引擎会根据匹配到的索引,在索引上找到对应的值,然后根据匹配上的索引记录找到对应的数据行.

这个过程就像,我们尝试在一本书上找到指定知识点的内容,会先翻到书的“目录”,然后根据指定“目录”找到对应的页码.

CREATE TABLE hero (

id int primary key auto_increment,

name varchar(50) not null,

hero_desc varchar(200),

key name_index(name)

)ENGINE = INNODB;

INSERT INTO hero VALUES(1,‘张三’,‘法外狂徒…’);

INSERT INTO hero VALUES(2,‘李四’,‘…’);

INSERT INTO hero VALUES(3,‘王五’,‘…’);

INSERT INTO hero VALUES(4,‘赵六’,‘…’);

INSERT INTO hero VALUES(5,‘冯七’,‘…’);

INSERT INTO hero VALUES(6,‘莫八’,‘…’);

INSERT INTO hero VALUES(7,‘莫九’,‘…’);

复制代码

image.png

假如我们要查找“name”为“张三”的数据.会先根据name_index 索引 查找到name=‘张三’ 的数据行的主键id,然后通过 id = 1查询主键索引 找到指定的数据行返回

select * from hero where name = ‘张三’;

复制代码

索引的类型


索引的类型有很多,我们可以根据不同的场景选择不同的索引.mysql的索引是在存储引擎层使用的.不同的存储引擎实现索引的方式也不相同.每个存储引擎支持的索引也不相同,也不是所有的存储引擎都支持所有的索引类型.

我们常用的存储引擎一般是INNODB , 如果没有特殊说明,使用的索引默认为B-Tree 索引.

B-Tree 索引

B-Tree 使用 B+ 树数据结构来存储数据.大多数的mysql 存储引擎都支持这种索引.存储引擎以不同的方式使用B-Tree索引,性能也各有不同.例如MyISAM 引擎使用前缀压缩技术来使索引更小,而InnoDB 则按照原始数据格式进行存储.MyISAM索引通过数据存储的物理位置来引用索引列,而InnoDB则 根据主键来引用索引列.

B-Tree 通常意味着所有的值都是按顺序存储的,并且每一个叶子页到跟的距离相同.

建立在 B+ Tree数据结构上的索引(InnoDB引擎):

image.png

B-tree能够加快数据访问的速度,因为存储引擎不再需要进行全表扫描来找到指定的数据.它从树结构的根结点出发,跟节点种存放了指向子节点的指针,存储引擎根据这些指针向下层查找.通过比

较节点页的值和要查找的值可以找到合适的指针进入下一层子节点.这些指针实际上定义了子节点页中值的下限和上限.存储引擎要么最终找到要查找的值,要么该值不存在.

叶子节点不用于子节点,它的指针指向的是被索引的数据,可能是指向聚簇索引的指针或数据行.

可以使用B-Tree 索引 的查询类型:

B-Tree 适合用于全键值,键值范围或键前缀查找.其中键前缀查找只适用于键最左前缀的查找.

创建一张商品表.创建主键索引,单值索引,复合索引.

create table product(

id int primary key auto_increment,

name varchar(50) not null comment ‘商品名称’,

category_id int not null comment ‘分类id’,

price decimal not null comment ‘价格’,

key product_name(name),

key category_and_name(category_id,name)

)engine = innodb;

复制代码

全值匹配:

全值匹配指的是和索引中所有列进行匹配.如:

select * from product where name = ‘笔记本’;

复制代码

image.png

select * from product where category_id = 1 and name = ‘阿巴阿巴’;

复制代码

image.png

匹配最左前缀:

最左前缀适用于复合索引(多列索引),如上面的category_and_name 索引.

最左前缀,故名思义,从左到右匹配索引.如 a ,b c 字段建立复合索引(a,b,c):

那么索引在什么时候能生效呢?

a ,

a b ,

a b c

select * from product where category_id = 1;

复制代码

image.png

匹配列前缀:

匹配某一索引列的值的开头的部分,like ‘a%’,like ‘b%’ . startWith

select * from product where name like ‘笔记%’;

复制代码

image.png

匹配范围值:

对索引列的范围查找,如 between , > < ,≥,≤

select * from product where id < 5;

复制代码

image.png

select * from product where id between 1 and 5;

复制代码

image.png

精确匹配某一列并范围匹配另一列:

select * from product where name = ‘笔记本’ and category_id > 1;

复制代码

image.png

只访问索引的查询:

即覆盖索引 , 不用再回表查询数据行 , 使用explain 分析时,extra 列 为 Using index;

select name from product where name = ‘笔记本’ ;

复制代码

image.png

B-Tree 索引采用B+ Tree的数据存储结构,因此索引树中的节点是有序的,所以除了按值查找之外,索引还可以用户查询中的Order By 操作(按顺序查找); 一般来说,如果B-Tree 可以按照某种方式查找到值,那么也可以按照这个方式进行排序.

所以说Order by 子句如果满足上面几种查询类型,则这个索引也可以满足对应的排序需求.

上面说了几种可以使用索引的查询类型,遵循上述的查询规则可以合理的使用索引,提高查询效率.

哈希索引

哈希表是基于哈希表实现的 , 只有精准匹配索引所有列的查询才有效.

对与索引列的每一行数据,存储引擎都会为其计算出来一个hash码,哈希码是一个较小的值,不同键值的行计算出来的hash码也不一样.哈希索引将所有哈希码存储在索引中,同时在哈希表中保存一个每个数据行的指针.如果发生了hash碰撞就会以链表的形式存放在一个hash条目中.

在mysql中,只有Memory引擎显式支持哈希索引.在这里我们不过多的去解释他.

空间数据索引

MyISAM引擎支持空间索引,可以用作地理数据存储.

与B-Tree索引不同(主要与存储结构有关),它无需前缀索引.空间索引会从所有维度来索引数据.查询时,可以有效地使用任意维度来组合查询.必须使用MYSQL的GIS相关函数如 MBRCONTAINS()等来维护数据.MySQL·对GIS的支持并不完善,所以这个索引基本不会被使用.

开源关系库系统中对GIS的解决方案做得比较好的是PostgreSQL的 PostGIS;

全文索引

全文索引是一种特殊类型的索引,他查找的是文本中的关键词,而不是直接比较索引中的值.全文索引更类似于搜索引擎做的事情,而不是简单的where匹配,这里不去过多解释它.

使用索引有哪些好处?


最直接的,索引可以帮助我们从大量数据行中快速的找到我们需要的数据.像最常见的B-Tree索引,因其存储结构,可以帮助我们进行Order by 和 Group by操作.总结以下优点:

1.大大减少了服务器所需要扫描的数据行数,提高了查询的效率

2.避免了生成 temp table 以及file sorted

3.避免了随机I/O,将其转换为有序I/O

索引不是越多越好,数据量越大,建立,使用,维护索引的成本越大.当数据表体量较小时,直接全表扫描比查询索引更快(需要回表查询的时候).只有当索引帮助存储引擎快速查找到记录带来的好处大于其带来的额外工作时,索引才是最有效的.

如果创建高性能的索引?


遵循索引的特性,避免索引失效.

索引失效的情况:

使用 or (除非所有or字段都添加了索引)

不满足最佳左前缀原则.

like 查询时 以 % 开头.

匹配字符串时不加“”号,需要类型转换

使用范围查询后,后边的查询条件都不能使用索引匹配

查询条件中索引列使用了函数;

mysql优化器优化后,认为全表扫描更快时

前缀索引和索引选择性

有时候需要索引很长的字符串,这会让索引变的很大,并且越来越慢.

通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提升索引效率.但这样也会降低索引的选择行.

索引的选择性是指,不重复的索引值(也称为索引基数) 和数据表的记录的总条目(#T)的比值. 范围从 1/#T 到 1 之间.

索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的行(降低索引命中行数).

比如说主键索引,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的.

InnoDB引擎的前缀长度可以达到767字节,如果启用了innodb_large_prefix选项,则可以达到3072字节.

MyISAM引擎的前缀限制的1000字节.text,blob,或者很长的varchar类型的列必须使用前缀索引

前缀索引最好保证较高的选择性的同时也不能太长.

前缀索引的基数应该趋近于完成列的基数,

1.索引列基数 ≈ 完整列的基数 (索引列基数/完整列的基数≈1)

2.索引基数 / 索引总条目数≈ 完整列基数 / 完整列总条目数

前缀索引对覆盖索引的影响

使用前缀索引将无法利用覆盖索引的优化。

查询时,系统并不确定前缀索引的定义是否截断了完整信息。

复合索引(多列索引)


索引能够同时覆盖多个数据列,对于复合索引来说:

mysql从左到右的使用索引中的字段,一个查询可以只使用索引的一部分,但是只能从最左侧开始.

例如:

我们定义了复合索引 index(c1,c2,c3),则我们进行查找的时候可以 c1 , c2 ,c3 | c1 ,c2 | c1 这三种组合来查找,只能从最左边来开始,

如果使用c2 , c3 进行查找则索引会失效.当最左侧字段是常量引用时,索引就十分有效.

image.png

当我们需要频繁的用到某些字段并且我们能确定使用字段的顺序时,我们就可以创建复合索引;12

但如果我们并不确定要用到哪些字段时就只能单独的为这些字段添加索引,添加无用的复合索引会引起索引失效的同时给mysql更改添加删除带来压力.

复合索引对排序的优化:

我们要知道复合索引只会对与创建索引时的排序顺序完全相同或相反的 order by语句进行优化

索引列的顺序

索引的顺序是至关重要的,正确的顺序依赖于使用该索引的查询,并且同时需要考虑如何更好地满足排序和需要.

根据复合索引的最佳左前缀原则,意味着索引首先按照最左列进行排序,然后依次往后排列.

当不需要考虑排序和分组时,应该优先考虑把选择性高的索引列放在前面.

假如我们有这样一张章节表:

CREATE TABLE chapter (

id int(11) NOT NULL AUTO_INCREMENT,

name varchar(50) NOT NULL COMMENT ‘章节名称’,

category_id int(11) NOT NULL COMMENT ‘分类id’,

project_id int(11) NOT NULL COMMENT ‘项目 id’,

subject_id int(11) NOT NULL COMMENT ‘科目 id’,

PRIMARY KEY (id)

) ENGINE=InnoDB;

复制代码

按照业务需求,我们希望根据分类,项目,科目这些查询条件,查询出符合条件的章节,我们应该如何设计一个复合索引?

首先我们先尝试计算这些列的选择性.

得出选择性最高的列依次为subject_id,project_id,category_id,在不考虑分组和排序的情况下,索引应该建立为:

image.png

image.png

ALTER TABLE chapter

ADD INDEX chapter_category(subject_id, project_id, category_id)

复制代码

聚簇索引


聚簇索引

聚簇:表示数据行和相邻的键值紧凑地存储在一起。

聚簇索引 通过关键字 primary key 来声明,一个表只能有一个聚簇索引(覆盖索引可以模拟多个聚簇索引).

聚簇索引并不是一种索引类型,而是一种数据存储结构(B-Tree).

不同于Mysql中其他是 B-Tree类型索引,聚簇索引在叶子页中还保存了数据行,而其他B-Tree类型索引中叶子页存储的是主键id,也就是聚簇索引的key,当其查询结果不能满足当前查询时,会

通过“回表”操作,查询聚簇索引.

因为不是所有的存储引擎都支持聚簇索引,在这里我们主要关注InnoDB ,但原理针对于任何支持聚簇索引的存储引擎都是支持的.

下图展示了聚簇索引中的记录是如何存放的. 注意到,叶子页包含了行的全部数据,但是节点也只包含了索引列.

image.png

Innodb 的 主键索引被定义为聚簇索引, 如果一个表没有主动声明primary key ,InnoDB会选择一个唯一非空的索引代替,如果都没有,InnoDb会 隐式定义一个主键来作为聚簇索引.

InoDB只聚集同一个页面中的索引,相邻键值的页面可能会相距很远.

聚簇主键可能对性能有帮助,但也可能导致严重的性能问题.所以需要仔细地考虑聚簇索引,尤其是将表的存储引擎从InnoDb钙哼其他引擎的时候(反之亦然).

聚簇索引的优点:

可以把相关数据保存在一起

数据访问更快(聚集索引将索引和数据保存在同一个b-tree中)

使用覆盖索引扫描的查询可以直接使用页节点中的主键值

聚簇索引的缺点:

聚簇数据提高了IO性能,如果数据全部放在内存中,则访问的顺序就没那么重要了.

插入速度严重依赖插入顺序。按主键的顺序插入是速度最快的。但如果不是按照主键顺序加载数据,则需在加载完成后最好使用optimize table重新组织一下表.

更新聚簇索引列的代价很高。因为会强制InnoDB将每个被更新的行移动到新的位置.

基于聚簇索引的表在插入新行,或主键被更新导致需要移动行的时候,可能面临页分裂的问题。页分裂会导致表占用更多的磁盘空间.

聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或由于页分裂导致数据存储不连续的时.

非聚集索引比想象的更大,因为二级索引的叶子节点包含了引用行的主键列.

非聚集索引访问需要两次索引查找(非聚集索引中叶子节点保存的行指针指向的是行的主键值),对于innodb自适应哈希索引可以减少这样的重复工作.

InnoDB 和 MyISAM 的数据分布对比

聚簇索引与 非聚簇索引 的数据分布是有一定区别的,非聚簇索引的叶子结点存储的是聚簇索引的key,也就是主键id.

在使用非聚簇索引查询时,往往需要查询两边索引,先根据索引列查询到主键id,然后根据主键id从聚簇索引中查找到指定的数据行.

假如有这样一张表:


CREATE TABLE layout_test(

col1 int NOT NULL,

col2 int NOT NULL,

PRIMARY KEY(col1),

KEY(col2)

);

insert into layout_test values (99,8),(12,56),(3000,62),…(18,8),(4700,13),(3,93);

复制代码

MyISAM:

MyISAM 是不支持聚簇索引的,相对来说数据分布比较简单.

MyISAM的每个索引中叶子结点存储的都是指向数据行的地址值.我们用两张图大致来说明存储方式.

layou_test表的数据分布:

MyISAM 按照 数据插入的顺序存储在磁盘上.

image.png

col1 主键索引分布:

image.png

col2 普通索引分布:

image.png


这两个存储结构是一致的,除了节点存储的key 是根据索引列顺序排列的.

InnoDB:

InnoDB区分聚簇索引与非聚簇索引.

最后

面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典

  • Java核心知识整理

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

Java核心知识

  • Spring全家桶(实战系列)

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

  • 其他电子书资料

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

Step3:刷题

既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。

以下是我私藏的面试题库:

2020年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

ert/51d25f67dc09b03a51870327b0462128.webp?x-oss-process=image/format,png)

col2 普通索引分布:

image.png


这两个存储结构是一致的,除了节点存储的key 是根据索引列顺序排列的.

InnoDB:

InnoDB区分聚簇索引与非聚簇索引.

最后

面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典

  • Java核心知识整理

[外链图片转存中…(img-9WIIepwF-1715600350306)]

Java核心知识

  • Spring全家桶(实战系列)

[外链图片转存中…(img-GqO0wke7-1715600350306)]

  • 其他电子书资料

[外链图片转存中…(img-SjpJ8smU-1715600350307)]

Step3:刷题

既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。

以下是我私藏的面试题库:

[外链图片转存中…(img-XUGmuIDU-1715600350307)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值