MySQL索引原理

MySQL索引

索引讲解前的背景知识

讲B树索引之前,必须要先介绍树这种数据结构,一点一点剖析二叉树及B树的好处,做到深入浅出

树的简介

树跟数组、链表、堆栈一样,是一种数据结构。它由有限个节点,组成具有层次关系的集合。因为它看起来像一棵树,所以得其名。一颗普通的树如下:

树是包含n(n为整数,大于0)个结点, n-1条边的有穷集,它有以下特点:

  • 每个结点或者无子结点或者只有有限个子结点;

  • 有一个特殊的结点,它没有父结点,称为根结点;

  • 每一个非根节点有且只有一个父节点;

  • 树里面没有环路

一些有关于树的概念:

  • 结点的度:一个结点含有的子结点个数称为该结点的度;

  • 树的度:一棵树中,最大结点的度称为树的度;

  • 父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;

  • 深度:对于任意结点n,n的深度为从根到n的唯一路径长,根结点的深度为0;

  • 高度:对于任意结点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0

树的种类

按照有序性,可以分为有序树和无序树:

  • 无序树:树中任意节点的子结点之间没有顺序关系

  • 有序树:树中任意节点的子结点之间有顺序关系

按照节点包含子树个数,可以分为B树和二叉树,二叉树可以分为以下几种:

  • 二叉树:每个节点最多含有两个子树的树称为二叉树;(已练习二叉树的创建,二叉树的遍历先序遍历、中序遍历、后序遍历及层次遍历,二叉树的深度,高度及叶子节点)

  • 满二叉树:叶节点除外的所有节点均含有两个子树的树被称为满二叉树;

  • 完全二叉树:如果一颗二叉树除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布

  • 二叉查找树:首先它是一颗二叉树,若左子树不空,则左子树上所有结点的值均小于它的根结点的值;若右子树不空,则右子树上所有结点的值均大于它的根结点的值;左、右子树也分别为二叉排序树;

24075190-19d6cd149fd90ad1.png (284×184)24075190-3a9f238dd5254404 (325×318)

二叉查找树查询、添加、删除的优缺点:

查询:二叉查找树的查询效率介于O(log n)~O(n)之间,理想的排序情况下查询效率为O(log n),极端情况下BST就是一个链表结构(如上右图),此时元素查找的效率相等于链表查询O(n)。

二叉查找树在最差情况下竟然和顺序查找效率相当,这是无法仍受的。其实对于大多数情况,在正常使用中,BST的性能非常好,出现非常极端的退化情况的概率非常小

尽管如此,还是有机会出现这样的情况,可能有人就会想,可以像快速排序那样,在初始化的时候,将数据打乱即可

事实也证明,当存储数据足够大的时候,树的结构对某些关键字的查找效率影响很大。当然,造成这种情况的主要原因就是BST不够平衡(左右子树高度差太大)。

删除:二叉查找树需要注意的是删除节点操作时的不同情况,删除节点根据节点位置会有以下三种情况:

1.删除节点的度为0,则直接删除(一个结点含有的子结点个数称为该结点的度)

2.删除节点的度为1,则该子节点替代删除节点

3.删除节点的度为2,则从左子树中寻找值最大的节点替代删除节点。对树结构改动最少、节点值最进行删除节点值的必然是左子树中的最大叶子节点值与右子树中的最小叶子节点值

  • 平衡二叉树(AVL):一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

上面讲了下二叉查找树的缺点,为了解决这个问题,就需要通过一定的算法,将不平衡树改变成平衡树。因此,AVL树就诞生了。

如果确保每次插入和删除后树的高度都保持O(log n),则可以保证所有这些操作的效率都是O(log n)。

下面记录下平衡二叉树的插入和删除的之后,为了保持平衡需要做的操作

节点插入、旋转

AVL树插入节点的如下:

根据BST入逻辑将新节点插入树中

从新节点往上遍历检查每个节点的平衡因子,若发现有节点平衡因子不在[-1,1]范围内(即失衡节点u),则通过旋转重新平衡以u为根的子树

旋转的方式:

左旋转:用于平衡RR情况,对失衡节点u(unbalanced)及子树进行左旋

右旋转:用于平衡LL情况,对失衡节点u及子树进行右旋

左右旋转:用于平衡LR情况,对失衡节点失衡u的左子节点ul左旋,再对失衡节点u右旋

右左旋转:用于平衡情况,对失衡节点u失衡方向的右子节点ur右旋,再对失衡节点u左旋

AVL缺点

但AVL树可能在插入和删除过程中引起更多旋转。因此,如果应用程序涉及许多频繁的插入和删除操作,则应首选Red Black树(如 Java 1.8中的HashMap)。如果插入和删除操作的频率较低,而搜索操作的频率较高,则AVL树应优先于红黑树。

  • 红黑树:红黑树是一颗特殊的二叉查找树,

每个节点都是黑色或者红色,

根节点、叶子节点是黑色。如果一个节点是红色的,则它的子节点必须是黑色的。

从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点

这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长,结果是这个树大致上是平衡的

操作代价分析:

     (1) 查找代价:由于红黑树的性质(最长路径长度不超过最短路径长度的2倍),可以说明红黑树虽然不像AVL一样是严格平衡的,但平衡性能还是要比BST要好。其查找代价基本维持在O(logN)左右,但在最差情况下(最长路径是最短路径的2倍少1),比AVL要略逊色一点。

    (2) 插入代价:RBT插入结点时,需要旋转操作和变色操作。但由于只需要保证RBT基本平衡就可以了。因此插入结点最多只需要2次旋转,这一点和AVL的插入操作一样。虽然变色操作需要O(logN),但是变色操作十分简单,代价很小。

    (3) 删除代价:RBT的删除操作代价要比AVL要好的多,删除一个结点最多只需要3次旋转操作。

    RBT 效率总结 : 查找 效率最好情况下时间复杂度为O(logN),但在最坏情况下比AVL要差一些,但也远远好于BST。

                           插入和删除操作改变树的平衡性的概率要远远小于AVL(RBT不是高度平衡的)。因此需要的旋转操作的可能性要小,而且一旦需要旋转,插入一个结点最多只需要旋转2次,删除最多只需要旋转3次(小于AVL的删除操作所需要的旋转次数)。虽然变色操作的时间复杂度在O(logN),但是实际上,这种操作由于简单所需要的代价很小。

  • 霍夫曼树:带权路径最短的二叉树(不是重点略过)。

目的:

找出存放一串字符所需的最少的二进制编码

  • B树(B-树和B+树)

首先思考为什么会有B树?

大多数自平衡搜索树(如AVL和红黑树)都会假定所有数据都在主内存中,但我们必须考虑无法容纳在主内存中的大量数据。当键的数量很大时,将以块形式从磁盘读取数据,与主存储器访问时间相比,磁盘访问时间非常高。 B树是一种自平衡搜索树,设计的主要思想是减少磁盘访问次数。大多数树操作(增、删、查、最大值、最小值等)都需要都需要O(h)磁盘访问,h为树的高度。B树通过在节点中放置最大可能的键来保持B树的高度较低。通常,B树节点的大小保持与磁盘块大小相等。由于B树的高度较低,因此与平衡的二叉搜索树(如AVL树、红黑树等)相比,大多数操作的磁盘访问次数显著减少。

磁盘块是一个虚拟的概念, 是操作系统(软件)中最小的逻辑存储单位,操作系统与磁盘打交道的最小单位是磁盘块。

B-树 简介

B-树,也称为B树,是一种平衡的多叉树(可以对比一下平衡二叉查找树),它比较适用于对外查找。看下这几个概念哈:

  • 阶数:一个节点最多有多少个孩子节点。(一般用字母m表示)

  • 关键字:节点上的数值就是关键字

  • 度:一个节点拥有的子节点的数量

一颗m阶的B-树,有以下特征:

  • 根结点至少有两个子女;

  • 每个非根节点所包含的关键字个数 j 满足:⌈m/2⌉ - 1 <= j <= m - 1.(⌈⌉表示向上取整)(比如m为4,则非根节点包含的关键字的个数为1<=j<=3)

  • 有k个关键字(关键字按递增次序排列)的非叶结点恰好有k+1个孩子。

  • 所有的叶子结点都位于同一层。

B+ 树简介

B+树是B-树的变体,也是一颗多路搜索树。一棵m阶的B+树主要有这些特点:

  • 每个结点至多有m个子女;

  • 非根节点关键值个数范围:⌈m/2⌉ - 1 <= k <= m-1

  • 相邻叶子节点是通过指针连起来的,并且是关键字大小排序的。

一颗3阶的B+树如下:

B+树和B-树的主要区别如下:

  • B-树内部节点是保存数据的;而B+树内部节点是不保存数据的,只作索引作用,它的叶子节点才保存数据

  • B+树相邻的叶子节点之间是通过链表指针连起来的,B-树却不是。

  • 查找过程中,B-树在找到具体的数值以后就结束,而B+树则需要通过索引找到叶子结点中的数据才结束

  • B-树中任何一个关键字出现且只出现在一个结点中,而B+树可以出现多次。

B+树插入要记住这几个步骤:

  • 1.B+树插入都是在叶子结点进行的,就是插入前,需要先找到要插入的叶子结点。

  • 2.如果被插入关键字的叶子节点,当前含有的关键字数量是小于阶数m,则直接插入。

  • 3.如果插入关键字后,叶子节点当前含有的关键字数目等于阶数m,则插,该节点开始「分裂」为两个新的节点,一个节点包含⌊m/2⌋ 个关键字,另外一个关键字包含⌈m/2⌉个关键值。(⌊m/2⌋表示向下取整,⌈m/2⌉表示向上取整,如⌈3/2⌉=2)。

  • 4.分裂后,需要将第⌈m/2⌉的关键字上移到父结点。如果这时候父结点中包含的关键字个数小于m,则插入操作完成。

  • 5.分裂后,需要将⌈m/2⌉的关键字上移到父结点。如果父结点中包含的关键字个数等于m,则继续分裂父结点。

以一颗4阶的B+树为例子吧,4阶的话,关键值最多3(m-1)个。假设插入以下数据43,48,36,32,37,49,28.

  • 在空树中插入43、48、36,这时候跟节点拥有3个关键字,已经满了

  • 继续插入 32,发现当前节点关键字已经不小于阶数4了,于是分裂 第⌈4/2⌉=2(下标0,1,2)个,也即43上移到父节点。(即是操作4)

  • 继续插入37,49,前节点关键字都是还没满的,直接插入,如下:

  • 最后插入28,发现当前节点关键字也是不小于阶数4了,于是分裂,于是分裂, 第 ⌈4/2⌉=2个,也就是36上移到父节点,因父子节点只有2个关键值,还是小于4的,所以不用继续分裂,插入完成

B+树的查找

因为B+树的数据都是在叶子节点上的,内部节点只是指针索引的作用,因此,查找过程需要搜索到叶子节点上。还是以这颗B+树为例吧:

B+ 树单值查询

假设我们要查的值为32.

第一次磁盘 I/O,查找磁盘块1,即根节点(36,43),因为32小于36,因此访问根节点的左边第一个孩子节点

第二次磁盘 I/O, 查找磁盘块2,即根节点的第一个孩子节点,获得区间(28,32),遍历即可得32.

B+ 树范围查询

假设我们要查找区间 [32,40]区间的值.

第一步先访问根节点,发现区间的左端点32小于36,则访问根节点的第一个左子树(28,32);

第二步访问节点(28,32),找到32,于是开始遍历链表,把[32,40]区间值找出来,这也是B+树比B-树高效的地方。

B+树的删除

B+树删除关键字,分这几种情况

  • 找到包含关键值的结点,如果关键字个数大于⌈m/2⌉-1,直接删除即可;

  • 找到包含关键值的结点,如果关键字个数大于⌈m/2⌉-1,并且关键值是当前节点的最大(小)值,并且该关键值存在父子节点中,那么删除该关键字,同时需要相应调整父节点的值。

  • 找到包含关键值的结点,如果删除该关键字后,关键字个数小于⌈m/2⌉,并且其兄弟结点有多余的关键字,则从其兄弟结点借用关键字

  • 找到包含关键值的结点,如果删除该关键字后,关键字个数小于⌈m/2⌉,并且其兄弟结点没有多余的关键字,则与兄弟结点合并。

如果关键字个数大于⌈m/2⌉,直接删除即可;

假设当前有这么一颗5阶的B+树

如果删除22,因为关键字个数为3 > ⌈5/2⌉-1=2, 直接删除(⌈⌉表示向上取整的意思)

如果关键字个数大于⌈m/2⌉-1,并且删除的关键字存在于父子节点中,那么需要相应调整父子节点的值

如果删除20,因为关键字个数为3 > ⌈5/2⌉-1=2,并且20是当前节点的边界值,且存在父子节点中,所以删除后,其父子节点也要响应调整,因此20删除后,21移到父节点的右边界值。

如果删除该关键字后,关键字个数小于⌈m/2⌉-1,兄弟节点可以借用

如果删除15,删除关键字的结点只剩1个关键字,小于⌈5/2⌉-1=2,不满足B+树特点,但是其兄弟节点拥有3个元素(7,8,9),可以借用9过来,如图:

根据关键字16查到左边界,再查询7和10关键字,最后导10,15,删除15后,只剩下一个关键字,10,不够数量2,因此借用兄弟节点的9,并更新父节点的10为9

在删除关键字后,如果导致其结点中关键字个数不足,并且兄弟结点没有得借用的话,需要合并兄弟结点

主要流程酱紫:

  • 因为7被删掉后,只剩一个8的关键字,不满足B+树特点(⌈m/2⌉-1<=关键字<=m-1)。

  • 并且没有兄弟结点关键字借用,因此8与前面的兄弟结点结合。

  • 被删关键字结点的父节点,7索引也被删掉了,只剩一个9,并且其右兄弟结点(18,20)只有两个关键字,也是没得借,因此在此合并

  • 被删关键字结点的父子节点,也和其兄弟结点合并后,只剩一个子树分支,因此根节点(16)也下移了

所以删除关键字7后的结果如下:

到此为止,数据结构中的树的相关知识我们过了一遍,接下来我们正式开始介绍索引的知识啦!

在 MySQL 中,索引是在存储引擎层而不是服务器层实现的。

索引的类型

  • BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(平衡多路查找树),每次查询都是从树的入口root开始,依次遍历node,获取leaf。这是MySQL里默认和最常用的索引类型。

当人们谈论索引的时候,如果没有特别指明类型,那多半说的是 B-Tree 索引,它们使用 B-Tree 数据结构来存储数据。实际上很多存储引擎使用的是 B+Tree ,即每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。(而B树在前面的背景知识,已被我讲透)

B-Tree 索引能够加块访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。

叶子节点比较特别,它们的指针指向的是被索引的数据,而不是其他的节点页(不同引擎的 “ 指针 ” 类型不同)。根节点和叶子节点之间可能又很多层节点页,这和树的深度与表的大小直接相关。

B-Tree 对索引列是顺序组织存储的,所以很适合查找范围数据。
可以使用 B-Tree 索引的查询类型。

其中键前缀查找只适用于根据最左前缀的查询。前面所述的索引对如下类型的查询有效。因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的 ORDER BY 操作(按顺序查找)。

  • 全值匹配,和索引中的所有列进行匹配。(比如姓名和年龄作为索引列,则可用于查找姓名和年龄的人)
  • 匹配最左前缀。(比如姓名和年龄作为索引列,则可用于查找姓名叫李东升的人)
  • 匹配列前缀,只匹配某一列的值的开头部分。(比如姓名和年龄作为索引列,则可用于查找姓叫王的人)
  • 匹配范围值。(比如姓名和年龄作为索引列,则可用于查找姓叫李到赵的人)
  • 精确匹配某一列并范围匹配另外一列。(比如姓名和年龄作为索引列,则可用于查找姓名叫李东升且年龄在15-80的人)
  • 只访问索引的查询。
  • 因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的 ORDER BY 操作(按顺序查找)。

下面是一些关于 B-Tree 索引的限制:

  • 如果不是按照索引的最左列开始查找,则无法使用索引。
  • 不能跳过索引中的列。
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用所有优化查找。
  • InnoDB一棵B+树可以存放多少行数据?
  • 在计算机中,磁盘存储数据最小单元是扇区,一个扇区的大小是512字节。

  • 文件系统中,最小单位是块,一个块大小就是4k;

  • InnoDB存储引擎最小储存单元是页,一页大小就是16k。

因为B+树叶子存的是数据,内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中找到需要的数据;

假设B+树的高度为2的话,即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=根结点指针数*单个叶子节点记录行数。

  1. 假设一行数据是1K,则一页可以存储16K/1K=16个数据
  2. 非叶子节点内存可以存放多少个指针,假设主键ID设置为bigint,长度为8字节,而指针大小在InnoDb源码里设置为6字节,所以就是8+6=14字节,所以16Kb/14b=1170因此,一棵高度为2的B+树,能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树,能存放1170 *1170 *16 =21902400,也就是说,可以存放两千万左右的记录。B+树高度一般为1-3层,已经满足千万级别的数据存储。
  • 哈希索引

哈希索引(hash index)基于哈希表实现。

在此延伸一下何为哈希算法(对该知识了解的同学可以跳过)

  • 散列表

    散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。这个映射函数称做散列函数,存放记录的数组称做散列表。

  • 散列函数

    散列函数,顾名思义,它是一个函数。如果把它定义成 hash(key) ,其中 key 表示元素的键值,则 hash(key) 的值表示经过散列函数计算得到的散列值。

  • 散列函数的特点:

1.确定性

如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。

2.散列碰撞(collision)

散列函数的输入和输出不是唯一对应关系的,如果两个散列值相同,两个输入值很可能是相同的,但也可能不同。

3.不可逆性

一个哈希值对应无数个明文,理论上你并不知道哪个是。

“船长,如果一样东西你知道在哪里,还算不算丢了。”“不算。”“好的,那您的酒壶没有丢。”

4.混淆特性

输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。

  • 常见的散列函数(不重点讲解,感兴趣可以研究)

MD5、SHA1

  • 散列冲突

理想中的一个散列函数,希望达到如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2),这种效果,然而在真实的情况下,要想找到一个不同的 key 对应的散列值都不一样的散列函数,几乎是不可能的,即使是 MD5 或者 由美国国家安全局设计的 SHA-1 算法也无法实现。事实上,再好的散列函数都无法避免散列冲突。这涉及到数学中比较好理解的一个原理:抽屉原理。抽屉原理:桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面至少放两个苹果。对于散列表而言,无论设置的存储区域(n)有多大,当需要存储的数据大于 n 时,那么必然会存在哈希值相同的情况。这就是所谓的散列冲突。那应该如何解决散列冲突问题呢?

  • 常用的散列冲突解决方法有两类,开放寻址法(open addressing)和链表法(chaining)。
  • 开放寻址法

开放寻址法是一种解决碰撞的方法,对于开放寻址冲突解决方法,比较经典的有线性探测方法(Linear Probing)、二次探测(Quadratic probing)和 双重散列(Double hashing)等方法。

  • 线性探测方法

当我们往散列表中插入数据时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。

线性探测法一个很大的弊端就是当散列表中插入的数据越来越多时,散列冲突发生的可能性就会越来越大,空闲位置会越来越少,线性探测的时间就会越来越久。极端情况下,需要从头到尾探测整个散列表,所以最坏情况下的时间复杂度为 O(n)。

  • 二次探测方法

二次探测是二次方探测法的简称。顾名思义,使用二次探测进行探测的步长变成了原来的“二次方”,也就是说,它探测的下标序列为 hash(key)+0hash(key)+1^2[hash(key)-1^2]hash(key)+2^2[hash(key)-2^2]

  • 双重散列方法

所谓双重散列,意思就是不仅要使用一个散列函数,而是使用一组散列函数 hash1(key)hash2(key)hash3(key)。。。。。。先用第一个散列函数,如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找到空闲的存储位置。

事实上,不管采用哪种探测方法,只要当散列表中空闲位置不多的时候,散列冲突的概率就会大大提高。为了尽可能保证散列表的操作效率,一般情况下,需要尽可能保证散列表中有一定比例的空闲槽位。

一般使用加载因子(load factor)来表示空位的多少。

加载因子是表示 Hsah 表中元素的填满的程度,若加载因子越大,则填满的元素越多,这样的好处是:空间利用率高了,但冲突的机会加大了。反之,加载因子越小,填满的元素越少,好处是冲突的机会减小了,但空间浪费多了。

  • 链表法

    链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。如下动图所示,在散列表中,每个位置对应一条链表,所有散列值相同的元素都放到相同位置对应的链表中。

哈希索引只有Memory, NDB两种引擎支持,Memory引擎默认支持哈希索引,如果多个hash值相同,出现哈希碰撞,那么索引以链表方式存储。但是,Memory引擎表只对能够适合机器的内存切实有限的数据集。mysql采用CRC32算法,建立SHA或MD5算法是划算的,本身位数都有可能比email段长了。非常浪费空间,这里要考虑到性能的要求

只有精度匹配索引所有列的查询才有效。

对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code)。例如哈希函数f(Arjen)=2343,则2343对应的value指向第三行的指针

哈希码是一个较小的值,并且不同的键值的行计算出来的哈希码页不一样。

哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快。
然后,哈希索引也有它的限制:

  • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
    ​​
  • 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
  • 哈希索引页不支持部分索引列匹配查找,因为哈希索引时钟是使用索引列的全部内容来计算哈希值的。
  • 哈希索引只支持等值比较查询,不支持任何范围查询。
  • 访问哈希索引的数据非常快,除非有很多哈希冲突。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
  • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。

为这些限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则它带来的性能提升将非常显著。

InnoDB 引擎有一个特殊的功能叫做 “ 自适应哈希索引(adaptive hash index) ”。当 InnoDB 注意到某些索引值被使用得非常频繁时,它会在内存中基于 B-Tree 索引之上再创建一个哈希索引,这样就让 B-Tree 索引也具有哈希索引的一些优点,比如快速的哈希查找。这是一个完全自动的、内部的行为,用户无法控制或者配置,不过如果有必要完全可以关闭该功能。

创建自定义哈希索引。 如果存储引擎不支持哈希索引,则可以模拟像 InnoDB 一样创建哈希索引,这可以享受一些哈希索引的便利,例如值需要很小的索引就可以为超长的键创建索引。
 

空间数据索引(R-Tree)

  • MyISAM 表支持空间索引,可以用作地理数据存储。和 B-Tree 索引不同,这类索引无须前缀查询。空间索引会从所有维度来所有数据。查询时,可以有效地使用任意维度来组合查询。必须使用 MySQL 的 GIS 相关函数如 MBRCONTAINS() 等来维护数据。MySQL 的 GIS 支持并不完善,所以大部分人都不会使用这个特性。开源关系数据库系统中对 GIS 的解决方案做得比较好的是 PostgreSQL 的 PostGIS。

全文索引

  • 全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不一样。它有很多需要注意的细节,如停用词、词干和复数、布尔搜索等。全文索引更类似于搜索引擎做到的事情,而不是简单的 WHERE 条件匹配。在相同的列上同时创建全文索引和基于值得 B-Tree 索引不会有冲突,全文索引适用于 MATCH AGAINST 操作,而不是普通的 WHERE 条件操作。

索引的优点

前面我们讲到了索引的类型,接下来讲索引的优点

索引有许多优点,比如常见的B-TRee索引,是按照顺序存储数据,索引MySQL可以用来做ORDER BY和GROUP BY操作,因为数据是有序的,所以B-Tree也就会将相关的列值都存储在一起。当然因为索引中存储了实际的列值,所以有些查询不需要扫描表,只需要索引就可以完成查询(即SELECT column1而不是SELECT *)

索引的优点:

索引大大减少了服务器需要扫描的数据量。
索引可以帮助服务器避免排序和临时表。
索引可以将随机 I/O 变为顺序 I/O。
评价一个索引是否合适某个查询的 “ 三星系统 ”(three-star system):

获取一星:索引将相关的记录放到一起。
获得二星:如果索引中的数据顺序和查找中的排序顺序一致。
获得三星:如果索引中的列包含了查询中需要的全部列。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值