弄懂了 MySQL 的基本 CURD 操做以后,下一个必须掌握的知识就是 MySQL 的索引。html
我在面试中,常常喜欢针对 MySQL 的知识由浅入深地问下去,了解候选人对 MySQL 知识的了解到了哪个层级。上一篇文章中的那些知识太基础了,我是不会拿来问的。所以我会问的第一个问题必然是 MySQL 的索引。mysql
关于 MySQL 的索引,我大体会问下面几个问题:git
要回答这两个问题,咱们须要了解下面几个知识:引擎、索引、树程序员
MySQL 在设计之初,就容许嵌入不一样的引擎。数据库的核心算法其实是由引擎来实现的。早期 MySQL 数据库有如下三个主流引擎:面试
MyISAM
: 这是 MySQL 5.5 以前的默认引擎。因为其不支持事务处理,所以在新的系统中基本上没什么人用了。InnoDB
: 这是 MySQL 5.6 以及以后的默认引擎。若是你不知道应该选什么引擎的话,选它基本没错。Memory
: 这是一个特殊的引擎,该引擎存取的数据,所有放在内存中,不会落入磁盘。所以当数据库宕机或重启后,数据就会丢失。自从 Redis 兴起以后,memory 引擎也式微了。因为新系统中几乎都选用了 InnoDB 引擎,所以下文中如无特别说明,则指的均为 InnoDB 引擎下的软件原理和行为。算法
按照参考资料1 InnoDB 引擎的关键特性包括如下内容:sql
能够看到五个特性中,有四个特性是和存储直接相关的。学过计算机组成原理的话就会知道,计算机存储,根据其与 CPU 的距离由近到远有如下几个:数据库
其中寄存器对于程序员来讲通常是不须要关注的;缓存没法直接在程序中影响和操做。对于绝大部分的计算机程序所操做的存储为内存和硬盘。操做系统读取内存和硬盘的时候,基本上以 “页” 为单位进行操做的。segmentfault
为何须要以 “页” 为单位操做呢?我们先看内存:内存实际上是彻底能够随机读取的,也就是说 CPU 若是想要读取哪个地址上的数据,那么一条指令就能够取到。可是应用程序上存取的不是实际内存,而是虚拟内存。而操做系统映射虚拟内存,只能以页为单位进行映射。所以,即使你操做的是内存,仍是请自觉尽可能对齐页。api
重点则是硬盘。硬盘包括两种类型,一种是磁盘,也就是以磁性元件来存储数据的介质;另外一种是所谓的 SSD,也就是固态硬盘。关于磁盘的读取原理和过程,我以前写过两篇文章《高性能磁盘 I/O 开发学习笔记 -- 硬件原理篇》和《高性能磁盘 I/O 开发学习笔记 -- 软件手段篇》。简单而言,因为硬件原理的限制,硬盘的读写有如下两个特色:
MySQL 定位的是大量数据的数据存储。每个表中存储的数据目标是百万行起跳;数据数据结构较为简单,索引效率高的话,千万也没有问题。实际使用中,也有上亿的场景。这就像一个图书馆,咱们须要对每一本书进行标记和索引,这样在查找书目(数据)时,才可以高效地查询到所须要的数据。
索引的原理,本质上就是解决快速查找和快速修改的目的。其次则是解决很是纠结的硬盘写入流程,整个过程当中还须要各类防止崩溃和宕机——毕竟 MySQL 的数据一致性要求是很高的。
做为 MySQL,常常须要关注的数据结构有如下几个:哈希表、B-树、B+树。
哈希(hash)算法相信你们都了解了,本文就不赘述。哈希算法的时间复杂度为 O(1)。在 MySQL 中,前文提到的三个主要引擎只有 Memory 引擎在索引中使用了哈希算法。那为何其余引擎不是用这个算法呢?由于其余引擎须要考虑落地硬盘的问题啊。
哈希的算法虽然简单,可是哈希表在实际应用中须要考虑表的扩容和缩容的问题。当哈希表须要扩容/缩容的时候,整个表中的全部元素均可能须要从新排列。Memory 引擎是不落磁盘的,不 care。但即使是 Memory,也不适合存储大量数据。实际上在现实使用中,Memory 的使用场景已经不断被压缩,大部分已经被 Redis 所取代了。
B树的原理其实相对而言比较简单,它就是一棵树。B树相比起最基本的树结构来讲,比较特别的就是树的分裂和合并。主要就是在数据库的内容增长和减小的时候所发生。具体的过程读者能够查阅网上相关的资料,很是多。
B树的特色是:
不是 MySQL 的 MongoDB
使用的就是 B树。那么这里的问题是:为何采用 B树,而不是搜索效率更高的红黑树呢?(面试考点注意!)
须要注意的是,B树有时候也被称为B-树,可是有些文章中B树指的又不是B-树,而是二叉树(Binary Tree)。读者在识别这些用词的时候,要结合上下文区分。
B+树是本文的重点,由于 InnoDB 使用的树结构就是B+树。一个B+树的示意结构图以下:
看起来和B树是很像的,可是实际上有两个很是关键的差别:
前文提到,InnoDB 所使用的算法是B+树;B+树上的非叶子结点存储的只是数据结构的索引,用于定位子结点用的,而不是 “数据库索引”。
那么问题来了:InnoDB B+ 树的叶子结点保存的是什么呢?这就引出了第一个分类:
Clustered Index,中文翻译不一,有 “聚簇索引”、“汇集索引”、“聚类索引” 等。聚簇索引指的是在叶子结点上,存储的数据就是完整的 MySQL 的一行数据。
那么在B+树的内部,用什么来索引叶子结点呢?答案是主键(main key)。在实际应用中,很大一部分的表在建立的时候都会把第一列定义为 int
或者 bigint
类型,而且指定为 auto increment
类型并设定为主键。这是一个很是通用并且很是保险的作法。咱们联系一下前文 B+ 树的特性就能够发现,若是针对这个自增ID直接进行查询、或者是以自增ID为条件进行大于、小于等范围操做,都很是高效。
那么若是在建表的时候不明确指定自增ID的话,会怎样呢?B+树失效?
对于 MyISAM 引擎来讲,主键不是必须的,若是不指定主键,那就没有主键。可是在 InnoDB 中主键是必要的,若是不指定主键的话,那么 InnoDB 会隐含地添加一个 24 位宽的整型ID做为主键。但这会致使这个整型 ID 不可见,致使相关的一些操做好比 last inserted id
变得没有意义。所以在实际操做中咱们仍是须要显式地指定主键。
对于 InnoDB 来讲,聚簇索引能够等同于就是主键的索引。
Secondary Index,中文翻译也不一,有 “非聚簇索引”、“辅助索引”、“二级索引” 等。在非聚簇索引的叶子结点上,存储的是对应的那一行 MySQL 数据的主键。
若是经过非聚簇索引,也就是除了主键之外的字段查找到了条目以后,此时 InnoDB 仅仅拿到了两个数据:一个是当前节点的索引列的值;另外一个是主键。若是客户端还请求了其余数据的话,那么 InnoDB 须要再到当前表的聚簇索引中进行查阅。这个动做称为 “回表” 查询。
按照组成逻辑区分的话,InnoDB 索引能够分为:
在这里须要特别说明的是联合索引。笔者以前一直觉得联合索引就是索引了一个字段以后,在获得的结果中再对下一个字段进行索引。但后来查阅资料以后才知道其实并非。
当建立一个联合索引时,索引中的每个字段的值,都会在索引的数据结构中出现。这里我以为这篇文章讲得已经很是准确和简要了,读者能够直接参阅。
“覆盖索引” 并非一种索引的类别,而是一种查询状况。前文提到过,在大部分按照索引进行的查询时,还须要进行回表查询从而获得客户端所须要的其余字段。可是前文也提到,若是你查询的字段,当前的索引已经彻底覆盖了,那么这个时候 InnoDB 不会再进行多余的回表查询,而是在非聚簇索引查询中就直接把字段返回了。这个现象就称为 “覆盖索引”(covering index)。
InnoDB 在 5.7.4 labs 版本中开始支持对空间索引的支持。简单而言,咱们平时的索引就是一个纬度的,好比一个数字x。而空间索引则是对一个空间坐标系的索引,好比 (x, y) 或者是 (x, y, z)。
InnoDB 的索引采用 R 树,读者感兴趣的话能够参阅相关资料进一步学习。在大部分的应用场景中,若是不涉及地理数据的话,空间索引咱们用得仍是比较少的。
好了,前面的面试题,咱们就能够大体地回答出来了:
问: | InnoDB 索引所使用的算法是什么? |
答: | B+树 |
问: | 为何 InnoDB 要使用 B+ 树而不是其余的数据结构呢? |
答: | 相比起红黑树,B树的节点以页为单位,而页则与硬盘中的页相互绑定,所以能够优化硬盘存取的效率 |
相比起红黑树,B树的深度比较稳定,查找的耗时比较可预期——这个实际上是B树的分裂和旋转策略所决定的,读者能够进一步阅读资料了解 | |
相比起B树,B+树的叶子结点之间包含双向链表,能够极大地优化遍历类和 offset - limit 类查询的耗时 | |
InnoDB 在使用 B+树中,使用了非聚簇索引,这一算法能够极大地减小索引所占的空间,从而大大减小索引占用的内存和硬盘空间,提升索引重建效率 | |
其实这个答案不惟一,读者若是感兴趣还能够进一步阅读参考资料 | |
问: | 在 InnoDB 中,是否是必需要有主键?若是建表的时候不指定主键会怎样? |
答: | 前文已经回答了:主键是必须有的,若是不指定的话,InnoDB 会自动建立一个6字节的自增ID |
问: | InnoDB 的主键和索引有什么区别? |
答: | InnoDB 的主键是一种特殊的索引,也就是聚簇索引;而其余的索引都是非聚簇索引。区别就是聚簇索引上保存的是完整的一行数据,而非聚簇索引上保存的是索引值以及主键 |
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
原做者: amc,欢迎转载,但请注明出处。
原文标题:小面试官教你 MySQL——引擎、索引和算法
发布日期:2020-11-09
原文连接:https://cloud.tencent.com/developer/article/1336510,也是本人的博客