百度百科的解释:索引是对数据库表的一列或者多列的值进行排序一种结构,使用索引可以快速访问数据表中的特定信息。
优点:
缺点:
索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引,默认的索引类型为B+树索引。
因为两者数据结构上的差异导致它们的使用场景也不同,哈希索引一般多用于精确的等值查找,B+索引则多用于除了精确的等值查找外的其他查找。在大多数情况下,会选择使用B+树索引。
MySQL主要的索引类型主要有FULLTEXT,HASH,BTREE,RTREE。
B树和B+树最主要的区别主要有两点:
那在什么情况适合使用B树呢,因为B树的内部节点也可以存储值,所以可以把一些频繁访问的值放在距 离根节点比较近的地方,这样就可以提高查询效率。综上所述,B+树的性能更加适合作为数据库的索引。
聚簇索引和非聚簇索引最主要的区别是数据和索引是否分开存储。
在InnoDB存储引擎中,默认的索引为B+树索引,利用主键创建的索引为主索引,也是聚簇索引,在主索引之上创建的索引为辅助索引,也是非聚簇索引。为什么说辅助索引是在主索引之上创建的呢,因为辅助索引中的叶子节点存储的是主键。
在MyISAM存储引擎中,默认的索引也是B+树索引,但主索引和辅助索引都是非聚簇索引,也就是说索引结构的叶子节点存储的都是一个指向数据行的地址。并且使用辅助索引检索无需访问主键的索引。
上面是说了非聚簇索引的叶子节点存储的是主键,也就是说要先通过非聚簇索引找到主键,再通过聚簇索引找到主键所对应的数据,后面这个再通过聚簇索引找到主键对应的数据的过程就是回表查询,那么非聚簇索引就一定会进行回表查询吗?
答案是不一定的,这里涉及到一个索引覆盖的问题,如果查询的数据再辅助索引上完全能获取到便不需要回表查询。例如有一张表存储着个人信息包括id、name、age等字段。假设聚簇索引是以ID为键值构 建的索引,非聚簇索引是以name为键值构建的索引, select id,name from user where name = 'zhangsan';
这个查询便不需要进行回表查询因为,通过非聚簇索引已经能全部检索出数据,这就是索 引覆盖的情况。如果查询语句是这样, select id,name,age from user where name = 'zhangsan';
则需要进行回表查询,因为通过非聚簇索引不能检索出age的值。那应该如何解决那呢?只需要将索引覆盖即可,建立age和name的联合索引再使用 select id,name,age from user where name = 'zhangsan';
进行查询即可。
所以通过索引覆盖能解决非聚簇索引回表查询的问题。
对索引的优化其实最关键的就是要符合索引的设计原则和应用场景,将不符合要求的索引优化成符合索引设计原则和应用场景的索引。
除了索引的设计原则和应用场景那几点外,还可以从以几个方面考虑:
select * from table_name where a + 1 = 2
索引的使用场景、索引的设计原则和如何对索引进行优化可以看成一个问题。
创建索引:
使用CREATE INDEX 语句
CREATE INDEX index_name ON table_name (column_list);
在CREATE TABLE时创建
CREATE TABLE user(
id INT PRIMARY KEY,
information text, FULLTEXT KEY (information)
);
使用ALTER TABLE创建索引
ALTER TABLE table_name ADD INDEX index_name (column_list);
删除索引:
删除主键索引
alter table 表名 drop primary key;
删除其他索引
alter table 表名 drop key 索引名;
不一定,前面在索引的使用场景和索引的设计原则中已经提到了如何合理地使用索引,因为创建和维护 索引需要花费空间和时间上的代价,如果不合理地使用索引反而会使查询性能下降。
前缀索引是指对文本或者字符串的前几个字符建立索引,这样索引的长度更短,查询速度更快。 使用场景:前缀的区分度比较高的情况下。
建立前缀索引的方式:
ALTER TABLE table_name ADD KEY(column_name(prefix_length));
这里面有个prefix_length参数很难确定,这个参数就是前缀长度的意思。通常可以使用以下方法进行确定,先计算全列的区分度
SELECT COUNT(DISTINCT column_name) / COUNT(*) FROM table_name;
然后在计算前缀长度为多少时和全列的区分度最相似。
SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name;
不断地调整prefix_length的值,直到和全列计算出区分度相近。
最左匹配原则:从最左边为起点开始连续匹配,遇到范围查询(<、>、between、like)会停止匹配。 例如建立索引(a,b,c),大家可以猜测以下几种情况是否用到了索引。
第一种
select * from table_name where a = 1 and b = 2 and c = 3
select * from table_name where b = 2 and a = 1 and c = 3
上面两次查询过程中所有值都用到了索引,where后面字段调换不会影响查询结果,因为MySQL中的优化器会自动优化查询顺序。
第二种
select * from table_name where a = 1
select * from table_name where a = 1 and b = 2
select * from table_name where a = 1 and b = 2 and c = 3
第三种
select * from table_name where b = 1
select * from table_name where b = 1 and c = 2
答案是这两个查询语句都没有用到索引,因为不是从最左边开始匹配的。
第四种
select * from table_name where a = 1 and c = 2
这个查询语句只有a列用到了索引,c列没有用到索引,因为中间跳过了b列,不是从最左开始连续匹配的。
第五种
select * from table_name where a = 1 and b < 3 and c < 1
这个查询中只有a列和b列使用到了索引,而c列没有使用索引,因为根据最左匹配查询原则,遇到范围查询会停止。
第六种
select * from table_name where a like 'ab%';
select * from table_name where a like '%ab'
select * from table_name where a like '%ab%
对于列为字符串的情况,只有前缀匹配可以使用索引,中缀匹配和后缀匹配只能进行全表扫描。
在上面介绍了几种不符合最左匹配原则的情况会导致索引失效,除此之外,以下这几种情况也会导致索引失效。
select * from table_name where a = 1 or b = 3
select * from table_name where a + 1 = 2
select * from table_name where a = '1'
会使用到索引,如果写成 select * from table_name where a = 1
则会导致索引失效。select * from table_name where abs(a) = 1
select * from table_name where a != 1
select * from table_name where a is null
推荐阅读:
Java面试题(2022最新持续更新版)