索引?为什么会有mysql索引,解决了什么问题,底层原理是什么?为什么要用B+树作为解决方案?使用其他东西,比如哈希索引或B树,你不能吗?简单了解指数。首先,什么是指数?如果我告诉你索引是数据库管理系统中一种有序的数据结构,你可能会有些迷惑。为了避免这种情况,我打算举几个例子来帮助大家更容易地理解索引。我们在查字典的时候,可以根据词的部首和笔画找到对应的词,这样就可以快速的找到对应词所在的页面。字典开头的东西叫做索引。还有一个图书目录,可以帮助我们快速跳转到不同的章节。这时这里的目录也是一个索引。连景区的地图都会告诉你现在所在的位置,其他景点的位置。这张图也是一些方面的索引结合开头比较专业的讲解,你或许能理解什么是索引。为什么需要索引了解了索引的概念之后,我们要知道为什么需要索引呢?从刚才的例子我们可以看出索引的目的是:词典中的索引帮助我们快速找到单词书对应的目录,帮助我们快速跳转到需要看的章节。景区地图帮助我们快速的找到去往自己想要的景区的路。在数据库中,索引可以帮助我们快速查询对应的数据行,从而顺利的检索出所有列的数据。这个过程一定要快。对于现在的web应用来说,如果DB响应慢,会直接影响到整个请求的响应时间,对用户体验来说是灾难性的。对于点击一个按钮,等几秒再返回,那么用户很可能不会再使用你开发的应用。MySQL中的索引首先,MySQL与索引没有直接关系。Index其实是InnoDB中的一个概念,InnoDB是MySQL中使用的存储引擎。在InnoDB中,索引分为:聚簇索引非聚簇索引对于聚簇索引,它是InnoDB根据主键(PrimaryKey)构建的索引。可以暂时理解为key为主键,value为整行数据。而且一张表只能有一个聚簇索引。当然,您不必定义主键。但是一般情况下,我们会创建一个单调递增的主键,或者通过统一的ID生成算法来生成。如果没有定义主键,InnoDB将有自己的回退策略。InnoDB会选择第一个我们定义的所有值都不为空的唯一索引作为聚集索引。但是,在实际的生产环境中,确实存在这样的cornercase。InnoDB也有一个最终的底线。如果连唯一剩下的唯一索引都不满足要求,InnoDB会创建一个隐藏的6字节主键RowID,然后根据这个隐藏主键生成聚簇索引。至于非聚集索引,它是根据指定的列创建的索引,也称为二级索引(SecondaryIndex)。一个表最多可以创建64个二级索引。key是创建二级索引的列的值,value是主键。也就是说,如果通过非聚集索引查询,只能得到索引列本身的值+主键的值。如果要得到完整的列数据,需要根据得到的主键再次查询聚簇索引,这个过程被回调回表。让我在这里解释一下。现在有很多博客说MySQL使用InnoDB时,一张表最多只能创建16个索引。首先,这是错误的。明明是其他地方直接copy过来的,我什么都没做。核实。MySQL官方文章中明确指出,一张表最多可以创建64个非聚集索引,并且在创建非聚集索引时,列数不能超过16。注意,创建非聚集索引的列数-聚集索引不能超过16!顺便说一句这也是题外话,所谓技术严谨,什么是严谨?对于你通过其他渠道获得的知识,顶多是作者的观点,我们抱着怀疑的态度,自己想办法证明。验证后才成为事实。不是死记硬背某些名词,新玩意层出不穷,但追根溯源,你会发现确实如此。索引的底层原理前面提到了InnoDB中的索引类型,简单了解一下它的分类和区别。InnoDB中的索引是如何加速查询的?基本原理是什么?InnoDB中索引的底层结构是B+树,是B树的变种。让我向您展示B+树的样子。下图是一棵存储数字“1-7”的B+树。可以看出,在B+树中,每个节点可以有多个子节点,而在我们通常熟悉的二叉树中,每个节点最多只能有2个。而且,在B+树中,存储的数据是节点是有序的,有序的数据结构可以让我们进行快速的精确匹配和范围查询。而且,B+树中叶子节点之间有一个指向下一个节点的指针,而B树中没有叶子节点。在MySQLInnoDB的实际实现中,页节点实际上是一个双链表,存放的是指向上一个节点和下一个节点的指针。下图是一个包含整数“1-7”的B树。该图应该可以帮助您更好地了解两者之间的区别。而且,在B+树中,除了叶子节点存储真实数据外,其余节点只存储指向下一个节点的指针。换句话说,数据都在叶节点上。在B树中,所有节点都可以存储数据,这是主要区别。了解了B-tree和B+树的基本结构是什么样子之后,我们就需要了解InnoDB是如何使用B+树来存储数据的。首先,MySQL不在内存中存储数据。内存仅用作运行时优化。关于InnoDB内存架构,之前写过一篇文章。如果您有兴趣,可以先阅读。InnoDB会将数据存储在磁盘上,当我们查询数据时,OS会将存储在磁盘上的数据逐页加载到内存中。这里的页面是操作系统管理内存的一种方式。当它加载数据到内存中时,它会根据页面的大小将数据加载到某个磁盘块上。在这里,你可以理解为B树中的每个节点都是一个磁盘块。由于B-tree和B+树在查找时都需要进行I/O操作将需要的节点加载到内存中,那么B+树相比B-tree有哪些优势呢?个人认为主要有三点。一是B+树可以减少I/O的数量。为什么?因为数据结构长度差不多,B+树能减少I/O的次数吗?前面说过,单个节点代表一个磁盘块,单个磁盘块的大小是固定的。只有B+树的叶节点存储值。与所有节点都存储完整数据的B树相比,B+树中单个磁盘块可以容纳更多的数据。在单个磁盘块容量固定的前提下,存储元素的大小越小,可以存储的元素越多。也就是说,一次I/O可以加载更多的数据到内存中,而这些多次加载的元素很有可能被你用到,这在一定程度上可以减少I/O的次数。另外,单个节点可以存放更多的元素,也可以降低树的高度。二是查询效率更稳定。什么更稳定?那么在相同数据量的情况下,查询时间不会因为你查询的数据的不同ID而有很大的差异。也就是说,这个请求可能用了10ms,下一次同样的请求点击一次就用了20ms,这是很不能接受的。合写界面的性能取决于你数据库的心情?那为什么说使用B+树可以达到稳定的查询效率呢?因为B+树不是叶子节点,不会存储任何数据,所以要想得到最终的数据,就必须找到叶子节点。换句话说,每个查询的I/O数是相同的。由于B-tree的所有节点都可以存储数据,所以可能有的数据在一次I/O中查询到,而有的则需要查询到叶子节点才能找到数据,这样会导致查询效率不稳定。三是更好地支持范围查询。B-tree为什么不能很好的支持呢?让我们回到B树的图片。假设我们需要查询[3,5]区间内的数据,会发生什么?废话不多说,直接上图。可以看出,如果仍然没有查询到完整的数据到叶子节点,就会回到根节点重新遍历。另一方面,在B+树中,当找到叶子节点时,可以直接通过叶子节点之间的指针遍历链表,这样可以大大提高范围查询的效率。知道了这些,举一反三,就知道InnoDB为什么不使用Hash作为底层数据结构了。即使查询时Hash的时间复杂度甚至可以达到O(1),最后讲I/O整篇文章都提到了很多I/O,而在MySQL的索引设计中,需要减少I/O的数量尽可能多的I/O。什么?因为I/O很昂贵。当我们执行I/O时,会发生什么?本来想详细说说磁盘结构的,但是看了一眼空间,差不多就结束了,这里简单聊聊。在机械硬盘中,一个I/O操作包括三个步骤:首先,需要寻道,寻道是指磁盘磁头移动到磁盘上的磁道。这个时间一般在3-15ms以内。然后是旋转,磁盘会将存储相应数据的磁盘旋转到磁头底部,这又需要2ms左右,具体延时与磁盘的旋转速度有关。最后是数据传输。一波操作后,成本在10ms左右。不要以为10ms就可以了……比起SSD(固态硬盘)和内存的微秒、纳秒,简直是天壤之别。这就是为什么在MySQL中,随机I/O对其查询性能有很大的影响。
