1

1.索引的本质

Mysql 官方对索引的定义为:索引(Index)是帮助Mysql高效获取数据的数据结构。从中可以得出索引的本质:索引说白了就是一种数据结构;

数据库查询是数据的重要功能之一,最基本的查询算法是:顺序查找(linear search),显然在数据量很大的时候,效率是很低的。优化的查找算法如:二分查找法,二叉树查找等;虽然查找的效率提高了。但是各自的检索的数据都有要求:二分查找要求检索的数据必须是有序的,而二叉树查找只能应用在二叉查找树上;而数据本身的组织结构是不可能完全满足各种数据的结构的;所以,在数据之外,数据库系统还维护着满足特定的查找算法的数据结构。这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级的查找算法。这种数据结构就是索引。

在了解为什么要使用 B-tree这种外存数据结构前,先要了解一些硬件的知识:

2.外存储器-磁盘:

我们可以清楚知识,计算机一般有两种存储的方式:内存和外存;
内存:读写速度非常快,但是容量很小,而且造价非常贵,在不通电的情况下会数据会丢失,不能长期存储数据
外存:磁盘是相对常见的外存储设备,它是以存取时间变化不大为特征的。可以直接存取任何字符组,且容量大、速度较其它外存设备更快。

2.1 磁盘的构造

磁盘跟我们DVD那些其实是一个样的。。。盘面上有很多称为磁道的圆圈,数据就记录在上面。磁盘可以是单片的,也可以使若干盘片组成的盘组,每一盘片上有两个面。如下图6片盘组为例,除去最顶端和最底端的外侧面不存储数据之外,一共有10个面可以用来保存信息。

clipboard.png

当磁盘驱动器执行读/写功能时。盘片装在一个主轴上,并绕主轴高速旋转,当磁道在读/写头(又叫磁头) 下通过时,就可以进行数据的读 / 写了。

磁盘读取数据是以盘块(block)为基本单位的。位于同一盘块中的所有数据都能被一次性全部读取出来。而磁盘IO代价主要花费在查找时间Ts上。因此我们应该尽量将相关信息存放在同一盘块,同一磁道中。或者至少放在同一柱面或相邻柱面上,以求在读/写信息时尽量减少磁头来找时间Ts。

3.B-tree

B-tree 又叫平衡多路查找树。一颗m阶的B-tree(M叉树)的特性如下:

  1. 树的每个结点至多有M个孩子;

  2. 除根结点和叶子结点外,其它每个结点至少有有ceil(m / 2)个孩子;

  3. 若根结点不是叶子结点,则至少有2个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);

  4. 所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部结点或查询失败的结点,实际上这些结点不存在,指向这些结点的指针都为null);

clipboard.png

在B-Tree中按key检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。

4.B+tree

B+-tree:是应文件系统所需而产生的一种B-tree的变形树。

一棵m阶的B+-tree和m阶的B-tree的差异在于:

1.有n棵子树的结点中含有n个关键字; (B-tree是n棵子树有n-1个关键字)

2.所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (B-tree的叶子节点并没有包括全部需要查找的信息)

3.所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (B-tree的非终节点也包含需要查找的有效信息)

clipboard.png

Mysql索引使用的就是B+tree,而不是B-tree;那这又是为什么呢?

1) B+-tree的磁盘读写代价更低

B+-tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B-tree更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+-tree内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B-tree就比B+-tree多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。

2) B+-tree的查询效率更加稳定

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

其实mysql对数据存储,就是数据的持久化;而数据的持久化就是将数据写到硬盘;

若想提高mysql的读写有效的操作的就是减少磁盘的I/O操作;想要减少I/O操作,最好的方法就是遵循:局部性原理与磁盘预读

5.局部性原理与磁盘预读

由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:

当一个数据被用到时,其附近的数据也通常会马上被使用。

程序运行期间所需要的数据通常比较集中。

由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。


一只小蜗牛
318 声望12 粉丝