vlambda博客
学习文章列表

JAVA面试题之三—Mysql索引了解嘛?怎么优化查询效率?

Hash

只支持单条数据的查询。很多时候我们需要更复杂的操作。

BTree

特点

  • 不再是二叉搜索,而是N叉搜索,树的高度会降低,查询快

  • 叶子节点,非叶子节点,都可以存储数据,且可以存储多个数据

  • 通过中序遍历,可以访问树上所有节点

设计逻辑

  • 内存读写快,磁盘读写慢,而且慢很多

  • 磁盘预读:

    磁盘读写并不是按需读取,而是按页预读,一次会读一页的数据,每次加载一些看起来是冗余的数据,如果未来要读取的数据就在这一页中,可以避免未来的磁盘读写,提高效率(通常,一页数据是4K)

  • 局部性原理:

    软件设计要尽量遵循“数据读取集中”与“使用到一个数据,大概率会使用其附近的数据”,这样磁盘预读能充分提高磁盘IO效能

B+TRee

现在mysql使用的是这种索引

改进点以及优势

  • 仍然是N叉树,层级小,非叶子节点不再存储数据,数据只存储在同一层的叶子节点上,B+树从根到每一个节点的路径长度一样,而B树不是这样

  • 叶子之间,增加了链表(图中红色箭头指向),获取所有节点,不再需要中序遍历,使用链表的next节点就可以快速访问到

  • 范围查找方面,当定位min与max之后,中间叶子节点,就是结果集,不用中序回溯(范围查询在SQL中用得很多,这是B+树比B树最大的优势)

  • 叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;

    非叶子节点存储记录的PK,用于查询加速,适合内存存储

  • 非叶子节点,不存储实际记录,而只存储记录的KEY的话,那么在相同内存的情况下,B+树能够存储更多索引

问题1:索引类型中normal、unique、Fulltext什么意思?什么区别?

normal:表示普通索引

unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为unique

full textl: 表示 全文搜索的索引。FULLTEXT 用于搜索很长一篇文章的时候,效果最好。用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。

问题2:mysql5.7中使用的索引是 BTree?什么时候开始用的B+Tree?什么用Hash索引?

Hash索引

innoDB 引擎中不支持Hash索引。

hash索引一般用于内存数据库,比如memory存储引擎,数据放到内存里,为了加快速度,创建hash索引。

既然你提到InnoDB使用的B+ Tree的索引模型,那么你知道为什么采用B+ 树吗?这和Hash索引比较起来有什么优缺点吗?

B+ Tree索引和Hash索引区别

  • 哈希索引适合等值查询,但是无法进行范围查询

  • 哈希索引没办法利用索引完成排序

  • 哈希索引不支持多列联合索引的最左匹配规则

  • 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题

聚簇索引、覆盖索引

刚刚我们聊到B+ Tree ,那你知道B+ Tree的叶子节点都可以存哪些东西吗?

InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。

两种情况:如果存储了主键索引,就称之为聚簇索引;如果存储了主键的值而不是主键索引,那就称之为非聚簇索引。

在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。

聚簇索引查询会更快?

因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。

刚刚你提到主键索引查询只会查一次,而非主键索引需要回表查询多次。是所有情况都是这样的吗?非主键索引一定会查询多次吗?

(后来我才知道,原来这个过程叫做回表)

拿到索引之后,还需要到表根据索引查询一次数据的过程,叫做回表。

覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。

当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。

如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = ‘keytest’;的时候,就可以通过覆盖索引查询,无需回表。

再举个例子:

select id from table_name where name = ‘ZhangSan’.

这种情况下,可以根据 name 索引查询 id,id就是要查询的值,所以不用再查询聚簇索引。直接就能查询出结果。

联合索引、最左前缀匹配

那你们在创建联合索引的时候,需要做联合索引多个字段之间顺序你们是如何选择的呢?

我们把识别度最高的字段放到最前面。

在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。

举个例子:加入建了一个联合索引,使用 name 和 age 两列。

1)where name = ?

2) where age = ?

3) where name = ? and age = ?

上面会用到索引的有哪些?

答:1)3)

索引下推、查询优化

那你知道在MySQL 5.6中,对索引做了哪些优化吗?

引入了 Index Condition Pushdown(索引下推) MySQL 5.6引入了索引下推优化,默认开启,使用SET optimizer_switch = ‘index_condition_pushdown=off’; 可以将其关闭。官方文档中给的例子和解释如下:people表中(zipcode,lastname,firstname)构成一个索引

SELECT * FROM people WHERE zipcode=‘95054’ AND lastname LIKE ‘%etrunia%’ AND address LIKE ‘%Main Street%’;

如果没有使用索引下推技术

则MySQL会通过zipcode=’95054’从存储引擎中查询对应的数据,返回到MySQL服务端,然后MySQL服务端基于lastname LIKE ‘%etrunia%’ 和 address LIKE ‘%Main Street%’ 来判断数据是否符合条件。

如果使用了索引下推技术

则MYSQL首先会返回符合 zipcode=’95054’ 的索引,然后根据 lastname LIKE ‘%etrunia%’ 筛选出符合条件的索引后再返回到 MySQL 服务端,然后MySQL服务端基于address LIKE ‘%Main Street%’ 来判断数据是否符合条件,这样返回给MySQL服务端的索引数又会减少。有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。

你们创建的那么多索引,到底有没有生效,或者说你们的SQL语句有没有使用索引查询你们有统计过吗?

可以通过explain查看sql语句的执行计划,通过执行计划来分析索引使用情况

那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?

查询优化器 一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。

在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。

优化过程大致如下:

1、根据搜索条件,找出所有可能使用的索引

2、计算全表扫描的代价

3、计算使用不同索引执行查询的代价

4、对比各种执行方案的代价,找出成本最低的那一个

参考:

1)

2)mysql索引:https://zhuanlan.zhihu.com/p/73204847

实战:https://juejin.im/post/5decb37b6fb9a0161a0c267b