要去面试阿里,你至少要掌握如下的索引知识。

什么是索引?

索引其实就是一种数据结构,目的就是为了帮助我们快速的检索数据库中的数据。就好比我们查字典的时候,会先翻一番目录,这个目录就是索引。

索引具体采用的是哪种数据结构呢?

常见的MySQL中主要有两种索引:Hash索引和B+树索引。现在常用的存储引擎是InnoDB,它默认就是B+树。

你先说一说什么是Hash索引吧

哈希索引底层是哈希表,哈希表是一以key-value结构存储的,所以它是不连续的,也就不支持范围查找,也无法用于排序。同时Hash是一种非常快的查询方式,一般情况下的时间复杂度为O(1),也就是一般情况下只需要一次查询就能定位数据。

MySQL中Memory引擎支持Hash索引,因此查找操作非常快。

为什么InnoDB要用B+树作为默认索引?

我们都知道Tree这类数据结构的查询效率非常高,从算法逻辑上来讲BST二叉搜索树无论查找还是搜索次数都是最高的,它的时间复杂度是O(logn)。但是我们又不得不去考虑磁盘IO,索引都是存储在磁盘上的,针对大表来说,索引可能有几个G甚至更多。

当我们要利用索引的时候,显然不能把整个索引全部都加载到内存。我们能做的就是逐一加载每一个磁盘页,这里的磁盘页就指的是索引的树节点。所以树的高度决定了IO次数,也就决定了查询效率。

相对于二叉搜索树来说,B+树的优势就是高度不会太高,数据尽可能的冗余在节点上。每个节点冗余更多的数据,就可以有更多的数据被加载进内存。加载到内存进行比较,找下一个磁盘页的时间很短就可以忽略不计,所以它的查询效率更快。树不会太高也就减少了在插入删除时候,为了保证树平衡的旋转操作,所以B+树的插入删除效率也很高。

B+树的所有数据都保存在叶子节点上,因此查询单条数据的时候,查询速度非常稳定。对于关系型数据库来说,稳定的查询可以更好的估算数据库瓶颈。B+树的叶子节点上有指针相连,因此在做数据遍历的时候,只需要对叶子节点遍历就可以了。同时这个特性也使得B+树非常适合做范围查询。

InnoDB默认是B+树,那能说一说自适应哈希索引吗?

InnoDB存储引擎会监控表上各索引页的查询。如果观察到建立索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。

普通索引和唯一索引有什么区别?

除了字面意思之外,普通索引和唯一索引在查询和更新的时候有很大的区别。

为了减少随机读磁盘的的IO消耗,InnoDB引入了change buffer。当更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,不影响数据一致性的前提下,InnoDB会将这些“更新操作”缓存在change buffer中,这样就不需要从磁盘中读入这个数据页。而当我们下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中和这个页有关的操作,生成一个正确的版本然后返回结果。

如果一个查询操作命中普通索引,那么查到第一个满足条件的记录后,还不需要继续往下查找,直到第一个部门组的记录后停止。而唯一索引的话,由于索引定义了唯一性,查找到第一个不满足的条件后就立即停止。

从查询操作来看,唯一索引可能很美好查找到第一个记录后就立即停止,但事实上带来的性能差距却是微乎其微的。因为InnoDB的数据本身是以页为单位然后读入内存,通常来说一个数据页可以放进千个key。除非我们查找的记录刚好是这个数据页的最后一行,那么读取下一行记录就必须加载下一个磁盘页。而这种情况出现的概率又很低,所以我们也可以认为“找到下一个不满足条件”这个操作成本对于现在的CPU来说可以忽略不计。

如果执行的是一个更新操作,当数据页已经加载到了change buffer中,那么唯一索引和普通索引都可以直接把更新操作缓存在内存中,只是唯一索引需要判断有没有插入冲突,当然这个判断消耗CPU的时间也可以忽略不计;如果磁盘页不在内存中,对于普通索引来说直接将更新记录在change buffer执行语句就结束了。而对于唯一索引来说,需要将数据页读入内存,当判断到没有冲突之后才可以插入这个值。

既然提到change buffer,那你说一说他的应用场景吧。

经过上面你的分析,我们知道了change buffer可以加速更新操作,从而减少了随机读IO的消耗。change buffer主要内存中的操作,那么他的IO瓶颈就发生在将记录变动合并到到数据页的时候,这个操作叫merge。也就是说如果merge的时候change buffer上的记录的变动越多,那么我们的缓存命中率就越大。

对于写多读少的业务来说,一个页写完然后立马被访问的概率会很少,这时候change buffer的效果最好。

而对于读多写多的业务来说,如果一个操作写入了change buffer,但是又马上被访问,此时就会立即出发merge操作。change buffer的目的是为了减少随机IO,而这种情况下不但不会减少,反而增加change buffer的维护代价。所以这种业务模式下可以关闭change buffer。

InnoDB的聚簇索引和非聚簇索引有什么区别?

聚簇索引并不是一种单独索引类型,而是一种存储方式。InnoDB的聚簇索引实际上实在同一个结构中保存了B-Tree的索引和数据行。所以如果表有聚簇索引,那么数据是存放在索引的叶子页中。InnoDB通过主键聚集数据,这样使得一个表只能有一个聚簇索引,所以基于主键索引的查询性能很高,因为叶子节点就是需要的数据。

而非聚簇索引的叶子节点就是索引,但是保留了一个指向对应连接的数据块。所以基于非聚簇索引的查询,最后需要回到主键索引树查询剩余数据,这个过程叫做“回表”。

是不是所有的非聚集索引都需要回表?

如果一个索引已经包含了我们需要的所有数据列,那我们就不需要再回表查询额外的信息,我们又称这样的索引为“覆盖索引”。合理的建立覆盖索引,也是常见的SQL优化手段。

例如一个用户表我们建立了年龄和姓名的索引,当我们需要查询18岁的用户的姓名的时候如果使用:select age, name from user where age = 18;就可以通过覆盖索引查询,无需回表。

听说过“最左前缀匹配”吗?

索引可以是由一个列组成,也可以由多个列组成的联合索引。

如果一个表创建了联合索引,那么索引的任何前缀都会用于查询。因为B+树是有顺序的数据结构,当数据项是符合数据结构时候,B+树就会按照从左到右的顺序来建立搜索树。

            

 

比如上图创建一个a/b/c的联合索引index_a_b_c,如果我们要查找a=2 and b=1的数据。首先查询a=2的行,当定位到第一个a=2的行之后,继续往下不断的搜索,如果发现比2大这时候就停止。因为是按照顺序排序的当出现比2大的数的时候就说明已经全部找到了。这时候我们便获得了一个区间,这时候我们再在这个a=2的区间里找到b=1的数据即可。

所以说我们创建a/b/c的联合索引,在过滤a/b条件的时候就可以命中索引,为了验证我们的观点我们查看一下执行计划:

 

using index和key说明使用了index_a_b_c的覆盖索引。

那么如果我们只想过滤b=1的数据这时候会命中索引吗?我们继续搜索找到b=1好了继续往下找到,当出现比1大的数据的时候我们敢确定所有b=1的数据找到了吗?肯定是不敢的,因为b的顺序是基于a,所以b这一行的顺序它是一个轮回。我们无法找到一个b=1就停止,我们需要搜索到末尾才敢肯定所有b=1的数据我们已经找完了。既然全表扫描了,这时候索引也无法帮我们优化。

 

你了解“索引下推”吗?

索引下推(Index Condition Pushdown Optimization)是MySQL5.6的一个重要优化,它是数据回表时候的一个提前优化。

还是上图那个表,这时候我们增加一个字段d索引结构不变,这时候我们执行一条SQL语句:

select * where a=2 and c=1;

根据最左前缀匹配原则是无法命中索引,因为c=1需要全表扫描。虽然我们最终还是要回表进行全表扫描,但是我们可以再回表的时候少扫描一些数据。索然无法利用索引过滤所有数据,但是我们利用索引找出a=2的数据,回表的时候就可以减少全表扫描的数据。这时候我们查看一下执行计划:

需要全表扫描,但是extra不为using where而是Using index condition。这就是索引下推

什么时候明明创建了索引,但是执行的时候并没有通过索引?

首先当你创建了很多索引,具体选择使用哪个是优化器的工作。

优化器的选择索引的目的就是找到一个最优的执行方案,并且用最小的代价去执行语句。在数据库里面,扫描行数是影响执行代价的原因之一。因为扫描的行数越少,就意味着访问磁盘的次数越少,消耗的CPU资源也就越少。

例如:当我们对一张用户表的性别创建了索引,这时候我们要选择出所有男性用户的信息,通过索引找出所有的男性信息的id,然后回表查出整行数据,这个代价优化器也要计算进去。但是如果我们直接在主键索引上扫描,那就不需要回表,也就没有额外代价。

优化器会估算两个选择的代价,从结果上来看,优化器认为直接扫描主键索引更快。但也可能从执行时间上来看,这个选择并不是最优的。这就是为什么我们明明创建了索引,但是执行的时候并没有通过索引。

从另一个方面来看,我们创建索引的时候,一定要选择区分度高的列来创建。区分度越高,也就意味着可以更快的锁定数据库中的行。

 

最后,如果感觉对你有帮助就来个二连吧:关注、点赞!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值