一、索引是什么?


数据库的数据存储在磁盘中,当用户或者程序通过DBMS查询数据库某条记录时,就会将磁盘中对应的数据读取到内存中。但是问题是,假如所查询的表的数据量很大,而内存是有限的,在没有索引的情况下,查询将对整个表进行扫描,就需要多次IO读取磁盘中的数据,通过“遍历”数据块的方式找到需要的记录。这个性能消耗很大,并且会很慢。
索引就是用来解决这个问题。对一个字段添加索引,那么会维护这个字段的索引数据,当查找这个关键字段的数据,就会将索引数据加载到内存中去,通过索引数据找到这个关键字段,而在索引中每个关键字段会指向对应的记录,或者一个桶(桶中存储对应记录的指针)。具体数据结构可以通过B树、Hash表实现。

具体例子:
假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,可以存储100条记录。如果没有索引,查询将对整个表进行扫描,最坏情况下,所有数据页都不在内存中,需要读取log100(10^6)=10^4个页面,如果这些页面在磁盘上随机分布,需要进行10^4次I/0,假设磁盘每次I/O时间为10ms,则总共需要100s。如果对之简历B-Tree索引,则只需要进行log100(10^6)=3次页面读取,最坏情况下耗时30ms。

二、索引的实现


索引是在存储引擎中实现的。存储引擎是指内存存储、事务处理支持等不同的技术以及配套的相关功能。我的理解是,数据存储所用的数据结构的差异,使得支持不同的技术和功能,划分成不同的存储引擎。下面就来浅谈三种索引数据结构。

1.B+Tree索引

先来看一下B树的定义:

一棵m阶平衡树或者为空,或者满足以下条件:
① 每个结点至多有m棵子树
② 根结点为叶结点,或至少有两棵子树
③ 每个非叶结点至少有[m/2]棵子树
④ 从根结点到叶结点的每一条路径都有同样的长度,即叶结点在同一层次上

m阶平衡很好地适应了内存有限,DBMS分块读取数据这个特点。

看一下B+树

一棵m阶B+树是一棵平衡树,按下面方式组织
① 每个节点中至多有m-1个查找键值K1,K2,……,Km-1,m个指针P1,P2,……,Pm。
② 叶结点中的指针指向所查找的记录
两种情况:
a.查找键恰好是主文件的主键(非空、唯一),那么叶结点中的指针直接指向主文件中的记录。例如指针Pi指向查找键值为Ki的主记录。
b. 查找键不是主文件的主键,并且查找键值的顺序也不是主文件的顺序,那么叶结点中的指针指向一个桶,桶中存放指向具有该查找键值的主记录的指针。
③ 非叶结点的组织方式
对于指针P跟键值K有,Pi指向的子树中的所有查找键值均大于或等于Ki-1,而均小于Ki。

2.BTree索引

类似于B+树,区别在于,B树中的所有查找键值只出现一次。
在B+树中,每个查找键值都必须在叶结点中出现,为了组织多级索引,某些查找键值还必须在上层结点中出现。在B+树中,查找键值可以出现在任何节点上,但只能出现一次。
所以在B树中,查找键值如果出现在非叶结点,那么需要在它出现的地方附加上一个指向主记录的指针。

3.Hash索引

散列方法是指根据记录的查找键值,使用一个函数计算得到的函数值作为磁盘块的地址,对记录进行存储和访问。在数据库技术中,一般使用“桶”作为基本的存储单位,一个桶可以存放多条记录。

桶溢出解决方法
  • 溢出桶拉链法
    溢出的桶用指针链接在原来的桶的后面。
  • 开放式散列法
  • 线性探查法
    在溢出桶位置的下面顺序选择一个空闲的桶。
  • 再散列探查法
    如果桶溢出,则使用第二个散列方法再选择另外一个桶位置。

hash索引跟B树索引一个很大的区别是,hash索引只能进行确定值的搜索,而不能模糊搜索。因为hash索引搜索是通过得到关键值的hash方法值,来得到记录的地址,所以也不能通过hash索引进行排序。

三、索引的利弊

优点

  • 大大加快数据的检索速度 (最主要的原因)
  • 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义
  • 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间
  • 通过创建唯一索引,可以保证数据库表中每一行数据的唯一性

缺点

  • 创建索引和维护索引需要耗费时间,并且随着数据量的增加而增加
  • 索引需要占用物理空间
  • 当对表中的数据进行增加、删除和修改的时候,索引也需要动态维护,降低了数据的维护速度。

所以,是否创建索引需要根据具体需要,权衡索引的利弊来使用索引。

索引的适用情况

  • 经常需要搜索范围搜索(hash索引不支持范围搜索)的列,可以加快搜索的速度
  • 作为主键的列,强制该列的唯一性和组织表中数据的排列结构
  • 经常用在连接的列,这些列主要是一些外键,可以加快连接的速度
  • 经常需要排序的列

索引的不适用情况

  • 很少使用适用或者参考的列。因为很少使用到,查询速度并没有明显区别,相反由于增加索引而降低了系统的维护速度和增加了空间需求。
  • 只有很少数据值的列。列的取值很少,那么索引就起不了什么作用了,因为B树没有区分度,而hash则会大量重复。
  • 定义为test、image、bit数据类型的列不应该增加索引。这些数据量很大,或者取值很少。