一次 MySQL 索引面试,被面试官怼的体无完肤!

一次 MySQL 索引面试,被面试官怼的体无完肤!

以前有过一次面试,关于MySQL索引的原理及使用被面试官怼的体无完肤,立志要总结一番,而后一直没有时间(实际上是懒……),准备好了吗?python


20a59ca99fc80f8d92eb3849e1efd6ac.jpg


索引是什么?

数据库索引,是数据库管理系统(DBMS)中一个排序的数据结构,它能够对数据库表中一列或多列的值进行排序,以协助更加快速的访问数据库表中特定的数据。通俗的说,咱们能够把数据库索引比作是一本书前面的目录,它能加快数据库的查询速度。mysql

为何须要索引?

思考:如何在一个图书馆中找到一本书? 设想一下,假如在图书馆中没有其余辅助手段,只能一条道走到黑,一本书一本书的找,通过3个小时的连续查找,终于找到了你须要看的那本书,但此时天都黑了。为了不这样的事情,每一个图书馆才都配备了一套图书馆管理系统,你们要找书籍的话,先在系统上查找到书籍所在的房屋编号、图书架编号还有书在图书架几层的那个方位,而后就能够直接大摇大摆的去取书了,就能够很快速的找到咱们所须要的书籍。索引就是这个原理,它能够帮助咱们快速的检索数据。面试

通常的应用系统对数据库的操做,遇到最多、最容易出问题是一些复杂的查询操做,当数据库中数据量很大时,查找数据就会变得很慢,这样就很影响整个应用系统的效率,咱们就可使用索引来提升数据库的查询效率。算法

B-Tree和B+Tree

目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree做为索引结构, 我在这里分别讲一下:sql

B-Tree

即B树,注意(不是B减树),B树是一种多路搜索树。使用B-Tree结构能够显著减小定位记录时所经历的中间过程,从而加快存取速度。数据库

B-Tree有以下一些特征数据结构

  1. 定义任意非叶子结点最多只有M个子节点,且M>2。
  2. 根结点的儿子数为[2, M]。
  3. 除根结点之外的非叶子结点的儿子数为[M/2, M], 向上取整 。
  4. 每一个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)。
  5. 非叶子结点的关键字个数=指向儿子的指针个数-1。
  6. 非叶子结点的关键字:K[1], K[2], …, K[M-1],且K[i] <= K[i+1]。
  7. 非叶子结点的指针:P[1], P[2], …,P[M](其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树)。
  8. 全部叶子结点位于同一层。

有关b树的一些特性ide

  1. 关键字集合分布在整颗树的全部结点之中;
  2. 任何一个关键字出现且只出如今一个结点中;
  3. 搜索有可能在非叶子结点结束;
  4. 其搜索性能等价于在关键字全集内作一次二分查找。

B树的搜索:从根结点开始,对结点内的关键字(有序)序列进行二分查找,若是命中则结束,不然进入查询关键字所属范围的儿子结点;重复执行这个操做,直到所对应的节点指针为空,或者已是是叶子结点。函数

例以下面一个B树,那么查找元素43的过程以下:性能

根据根节点指针找到1八、37所在节点,把此节点读入内存,进行第一次磁盘IO,此时发现43>37,找到指针p3。

根据指针p3,找到4二、51所在节点,把此节点读入内存,进行第二次磁盘IO,此时发现42<43<51,找到指针p2。

根据指针p2,找到4三、46所在节点,把此节点读入内存,进行第三次磁盘IO,此时咱们就已经查到了元素43。

在此过程总共进行了三次磁盘IO。


d62a328f5845f47f7d14d822a4f98dd9.jpeg


B+Tree

B+Tree属于B-Tree的变种。与B-Tree相比,B+Tree有如下不一样点:

  1. 有n棵子树的非叶子结点中含有n个关键字(B树是n-1个),即非叶子结点的子树指针与关键字个数相同。这些关键字不保存数据,只用来索引,全部数据都保存在叶子节点(B树是每一个关键字都保存数据)。
  2. 全部的叶子结点中包含了所有关键字的信息,及指向含这些关键字记录的指针,且叶子结点自己依关键字的大小自小而大顺序连接。
  3. 全部的非叶子结点能够当作是叶子节点的索引部分。
  4. 同一个数字会在不一样节点中重复出现,根节点的最大元素就是b+树的最大元素。


a6c77e8a6f7d586e9d3f666a539b1688.jpeg


相对B树,B+树作索引的优点

  1. B+树的磁盘IO代价更低: B+树非叶子节点没有指向数据行的指针,因此相同的磁盘容量存储的节点数更多,相应的IO读写次数确定减小了。
  2. B+树的查询效率更加稳定:因为全部数据都存于叶子节点。全部关键字查询的路径长度相同,每个数据的查询效率至关。
  3. 全部的叶子节点造成了一个有序链表,更加便于查找。

关于MySQL的两种经常使用存储引擎MyISAM和InnoDB的索引均以B+树做为数据结构,两者却有不一样(这里只说两者索引的区别)。

MyISAM索引和Innodb索引的区别

MyISAM使用B+树做为索引结构,叶节点叶节点的data域保存的是存储数据的地址主键索引key值惟一,辅助索引key能够重复,两者在结构上相同。 所以,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是要找的Key存在,则取出其data域的值,而后以data域的值为地址,去读取相应数据记录 。所以,索引文件和数据文件是分开的,从索引中检索到的是数据的地址,而不是数据。

Innodb也是用B+树做为索引结构,但具体实现方式却与MyISAM大相径庭,首先,数据表自己就是按照b+树组织,因此数据文件自己就是主键索引文件。叶节点key值为数据表的主键,data域为完整的数据记录,所以InnoDB表数据文件自己就是主键索引(这也就是MyISAM能够容许没有主键,可是Innodb必须有主键的缘由)。第二个与MyISAM索引的不一样是InnoDB的辅助索引的data域存储相应数据记录的主键值而不是地址。换句话说,InnoDB的全部辅助索引都引用主键做为data域。

索引类型

普通索引:(由关键字KEY或INDEX定义的索引)的惟一任务是加快对数据的访问速度。

惟一索引: 普通索引容许被索引的数据列包含重复的值,而惟一索引不容许,可是能够为null。因此任务是保证访问速度和避免数据出现重复。

主键索引:在主键字段建立的索引,一张表只有一个主键索引。

组合索引:多列值组成一个索引,专门用于组合搜索。

全文索引:对文本的内容进行分词,进行搜索。(MySQL5.6及之后的版本,MyISAM和InnoDB存储引擎均支持全文索引。)

索引的使用策略及优缺点

使用索引

主键自动创建惟一索引。
常常做为查询条件在WHERE或者ORDER BY 语句中出现的列要创建索引。
查询中与其余表关联的字段,外键关系创建索引。
常常用于聚合函数的列要创建索引,如min(),max()等的聚合函数。

不使用索引

常常增删改的列不要创建索引。
有大量重复的列不创建索引。
表记录太少不要创建索引,由于数据较少,可能查询所有数据花费的时间比遍历索引的时间还要短,索引就可能不会产生优化效果 。

最左匹配原则

创建联合索引的时候都会默认从最左边开始,因此索引列的顺序很重要,创建索引的时候就应该把最经常使用的放在左边,使用select的时候也是这样,从最左边的开始,依次匹配右边的。

优势

能够保证数据库表中每一行的数据的惟一性。
能够大大加快数据的索引速度。
加速表与表之间的链接。
能够显著的减小查询中分组和排序的时间。

缺点

建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。
索引须要占物理空间,除了数据表占用数据空间以外,每个索引还要占用必定的物理空间,若是须要创建聚簇索引,那么须要占用的空间会更大,其实创建索引就是以空间换时间。
表中的数据进行增、删、改的时候,索引也要动态的维护,这就下降了维护效率。

验证索引是否可以提高查询性能

建立测试表index_test


6785d2102f8e962647fe53c303c5426c.png


使用python脚本程序经过pymsql模块,向表中添加十万条数据

import pymysqldef main():
    # 建立Connection链接
    conn = pymysql.connect(host='localhost', 
                           port=3306,
                           database='db_test',
                           user='root',
                           password='deepin',
                           charset='utf8')
    # 得到Cursor对象
    cursor = conn.cursor()
    # 插入10万次数据
    for i in range(100000):
        cursor.execute("insert into index_test values('haha-%d')" % i)
    # 提交数据
    conn.commit()if __name__ == "__main__":
    main()

在mysql终端开启运行时间监测:set profiling=1;


12d1e720e2f49f69ea141f4a70bb1733.png


查找第1万条数据ha-99999

select * from index_test where name='haha-99999';

查看执行的时间:

show profiles;


f078660dd15cc50315fc03d250093bd5.jpeg


  • 为表index_test的name列建立索引:
create index name_index on index_test(name(10));

再次执行查询语句、查看执行的时间:


88da5f783175a05ed224a87214dcd6a5.jpeg


能够看出合适的索引确实能够明显提升某些字段的查询效率。


原做者:是虎子呀
原文连接: 那些年被面试官怼的MySQL索引 - 是虎子呀的我的空间 - OSCHINA
原出处:OSCHINA

8498e61271744e9b7e2fd0dabff5a521.jpeg

相关文章
相关标签/搜索