索引文件是按照不同的数据结构来存储的,数据结构的不同也产生不同的索引类型:
- 全文索引
- 空间数据索引(R-Tree)
- 哈希索引
- 树索引
全文索引
全文索引主要用于海量数据的搜索,MySQL从5.6开始支持InnoDB引擎的全文索引,功能没有专业的搜索引擎如Sphinx或Solr丰富。需求比较简单时可以试用。
空间数据索引
R-Tree,空间索引可用于地理数据存储,需要GIS相关函数支持,由于MySQL的GIS支持并不完善,所以该索引方式在MySQL中很少有人使用。目前只有MyISAM存储引擎支持空间检索?
hash索引
hash索引基于哈希表来实现,哈希表是做数据快速检索的有效利器。对于要索引的列,存储引擎会计算出一一对应的哈希码,然后把哈希码存放在哈希表中作为key,value值是指向该行数据的指针。只有精确匹配索引所有列的查询才有效。哈希算法:也叫散列算法,就是把任意值(key)通过哈希函数变换为固定长度的 key 地址,通过这个地址进行具体数据的数据结构。
但一切hash算法都可能存在一个问题:数据冲突,或者数据碰撞,即哈希函数可能对不同的key会计算出同一个结果。解决方法,如链地址法,即用链表把碰撞的数据接连起来。计算哈希值之后,还需要检查该哈希值是否存在碰撞数据链表,有则一直遍历到链表尾,直达找到真正的 key 对应的数据为止。时间复杂度为O(1)
。
优势:只需比对哈希值,速度非常快,性能优势明显;
限制:
- 不支持任何范围查询,因为是基于哈希计算,只支持等值比较
- 哈希表是无序存储的,无法用于排序
- 在有大量重复键值情况下,哈希索引的效率也是极低,即哈希碰撞问题
- 不支持最左匹配原则
- 主流存储引擎不支持该类型,比如MyISAM和InnoDB。哈希索引只有Memory,NDB两种引擎支持。
树索引
二叉查找树
BST,binary search tree,二叉查找树是一种支持数据快速查找的数据结构,时间复杂度是O(lgn)
,支持范围查找。致命缺点:极端情况下会退化为线性链表,二分查找也会退化为遍历查找,时间复杂退化为O(N)
,检索性能急剧下降。在数据库中,数据的自增是一个很常见的形式,比如一个表的主键是 id,而主键一般默认都是自增的,如果采取二叉树这种数据结构作为索引,什么提到的致命缺点,即不平衡状态导致的线性查找的问题必然出现。因此,简单的二叉查找树存在不平衡导致的检索性能降低的问题,是不能直接用于实现MySQL底层索引的。
红黑树
二叉查找树存在不平衡问题,因此学者提出通过树节点的自动旋转和调整,让二叉树始终保持基本平衡的状态,就能保持二叉查找树的最佳查找性能。基于这种思路的自调整平衡状态的二叉树有 AVL 树和红黑树。
红黑树,自动左旋右旋节点以及节点变色,调整树的形态,使其保持基本的平衡状态,时间复杂度为O(logn)
,保证查找效率不会明显减低。
但红黑树并没有完全解决退化为链表的这个问题,虽然右倾趋势远没有二叉查找树退化为线性链表那么夸张,但是数据库中的基本主键自增操作,主键一般都是数百万数千万的,若红黑树存在这种问题,对于查找性能而言也是巨大的消耗,我们数据库不可能忍受这种无意义的等待的。
AVL树
更为严格的自平衡二叉树 AVL 树。因为 AVL 树是个绝对平衡的二叉树,因此他在调整二叉树的形态上消耗的性能会更多。AVL树不存在红黑树的右倾问题,大量的顺序插入不会导致查询性能的降低。
优点:
时间复杂度是O(lgn)
,不错的查找性能,不存在极端的低效查找的情况。
可以实现范围查找、数据排序。
问题
数据库查询数据的瓶颈在于磁盘 IO,如果使用的是 AVL 树,每一个树节点只存储一个数据,一次磁盘 IO 只能取出来一个节点上的数据加载到内存里。所以我们设计数据库索引时需要首先考虑怎么尽可能减少磁盘 IO 的次数。
磁盘IO的特点,从磁盘读取1B数据和1KB数据所消耗时间是基本一样,根据这个思路,在一个树节点上尽可能多地存储数据,一次磁盘 IO就多加载点数据到内存,即B树,B+树的的设计原理。
B 树
尽可能在一次磁盘 IO 中多读一点数据到内存,反映到树的结构就是,每个节点能存储的 key 可以适当增加。
B树优点:
优秀检索速度,时间复杂度:B 树的查找性能等于O(h*logn)
,其中h 为树高,n 为每个节点关键词的个数;
尽可能少的磁盘 IO,加快了检索速度;
可以支持范围查找。
B+树
B 树和 B+树有什么不同呢?
- B 树一个节点里存的是数据,而 B+树存储的是索引(地址),所以 B 树里一个节点存不了很多个数据,但是 B+树一个节点能存很多索引,B+树叶子节点存所有的数据。
- B+树的叶子节点是数据阶段用一个链表串联起来,便于范围查找。
B+树节点存储的是索引,在单个节点存储容量有限的情况下,单节点也能存储大量索引,使得整个 B+树高度降低,减少磁盘 IO。其次,B+树的叶子节点是真正数据存储的地方,叶子节点用有序链表连接起来,在范围查找时效率更高。因此MySQL的索引用的就是 B+树,B+树在查找效率、范围查找中都有着非常不错的性能。
B tree 和 B+ tree
B-Tree能加快数据的访问速度,因为存储引擎不再需要进行全表扫描来获取数据,数据分布在各个节点之中。
B+ tree 是B-Tree的改进版本,同时也是数据库索引索引所采用的存储结构。数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来说,进行范围查找时只需要查找两个节点,进行遍历即可。而B-Tree需要获取所有节点,相比之下B+Tree效率更高。
为什么索引结构默认使用B-Tree,而不是hash,二叉树,红黑树?
hash:虽然可以快速定位,但无序,IO复杂度高
二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),IO代价高
红黑树:树的高度随着数据量增加而增加,IO代价高
问:为什么官方建议使用自增长主键作为索引?
结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率。
绝大多数的存储引擎,比如MyISAM和InnoDB都支持这种索引,但是不同的存储引擎在具体实现时会稍有不同,比如MyISAM会使用前缀压缩的方式对索引进行压缩,InnoDB则不会。
总结
- B-Tree索引使用最广泛,主流引擎都支持。
- 哈希索引性能高,适用于特殊场合。
- R-Tree不常用。
- 全文索引适用于海量数据的关键字模糊搜索。