前言索引是一个比较抽象的东西,不像数据库运维人员,开发人员往往不需要对它了解那么深,只需要知道它是什么就可以了。心里有一个大概的轮廓就好了,这样可以帮助我们更好的使用索引,理解为什么要这样使用,这样就达到了我们的目的。因此,本文面向开发者,比较简单,请轻看。索引概述索引是存储在磁盘上(可能部分存储在内存中)的特殊文件。为什么它很特别?它是对数据库表中某一列或几列的值进行排序后,具有特殊搜索结构的文件。通过它,我们可以快速的从数据库表中读取需要的信息。这是一个非常抽象的描述,我们很难想象它是一种什么样的结构。假设我们有一张表,有100w条数据,ids从1到1000000排列,在没有索引的情况下,我们要查找id=666666的数据。由于乱序,所以不知道id=666666的数据藏在哪里。它只能一项一项地检查。如果运气不好,您可能会扫描到最后一行。我们所说的扫描过程,其实就是将数据从磁盘加载到内存中的过程。这就是我们通常所说的磁盘IO操作。磁盘IO是一个非常昂贵的操作。访问磁盘的成本大约是访问内存的十倍。大约一万次。所以,我们优化的出发点就是尽量减少IO。我们一开始想到如果100w的数据是有序的就好了,这样可以用二分法先扫描50w-100w的数据,再扫描50w-75w的数据。..这样就可以大大减少扫描次数,达到减少磁盘IO的目的。有没有更有效的方法?有一个经典的数据结构,二叉树,它长这个样子:它的原理、特点、时间复杂度计算等这里就不说了。有兴趣的可以搜索一下。这里只有一点要知道,它的搜索效率非常高,但是有一个缺点:每个节点存储的数据量非常小。这就导致同一个数量级的数据,在二叉树中必然会增加树的高度,而树高的增加会导致IO次数的增加。比如我们要获取1的数据,那么读取的顺序就会是7-5-2-1,先从磁盘中加载7的数据到内存中,再从磁盘中加载5的数据入内存,然后2,1,经过4次IO终于得到需要的数据。如果数据量更大,树的高度增加,IO的数量也会相应增加。这里有一个公式,高度与节点总数正相关。看到这里,我们明白树越短,节点数越少,需要的磁盘IO越少,效率越高。让树变矮怎么样?有了一定的数据量,自然每个节点挂载的数据越多,节点就会越少,树就会越短!B树的概念由此产生。B树是一棵多叉树。每个节点可以挂载多个子节点。看起来像这样:显然,这棵树很矮。其实mysql的InnoDB引擎使用的是B-tree的存储结构,更准确的说是B+树,是在B-tree的基础上改进的,只在叶子节点上存储表数据。而其他非叶子节点只存储索引的键。大概长这个样子:这样一个3层的B+树可以表示几千万条数据。要访问最终数据,只需要3个IO,这是性能上的巨大提升。事实上,树的根节点往往在内存中,因此访问磁盘的次数较少。下面我们通过主键索引和非主键索引来说明整个索引过程。首先有一张表,表结构是这样的:主键索引如上图所示,叶子节点存储表中整行数据,非叶子节点存储表的键值主键,我们用主键ID来查询某一项数据,比如查询ID=8的数据。先将非叶子节点的索引键值加载到内存中,产生一次IO,在P2指向的磁盘页中定位到ID=8的数据,然后系统再进行一次IO操作,将数据放入P2指向的磁盘页数据被加载到内存中,在内存中进行筛选,找到ID=8的数据返回给客户端。这样就完成了ID=8数据的索引查询。整个过程执行了两次IO操作。让我们再看看非主键索引。非主键索引在非主键索引中,name是索引字段。可以看出,与主键索引不同,非主键索引中的叶子节点存储的不是完整的表数据,而是表数据的主键ID值。索引查询过程和主键索引一样,先将非叶子节点的索引键值加载到内存中,找到指向的磁盘页,然后将磁盘页中的数据加载到内存中,过滤掉内存中所需的数据。该进程还执行两个IO操作。结束了吗?显然不是,因为非主键索引存储的只是一个主键ID值,而我们需要的是完整的表数据。我们通过两次IO操作得到主键ID值后,还要再过一遍主键才能通过索引过程得到完整的数据,也就是说非主键索引需要进行四次IO查找我们需要的数据的操作。通过非主键索引获取ID,然后执行主键索引的过程称为回表。你熟悉吗?了解了这些,就不难理解为什么不用select*,尽量用覆盖索引了。一切的初衷都是为了减少磁盘IO。
