MySQL 笔记 - 索引类型

写在前面

这,只是一篇读《高性能 MySQL》的读书笔记,以为水的同窗麻烦右上角关闭哈,感谢~算法

索引类型包括 B-Tree、哈希索引、R-Tree、全文索引等,这里主要总结 B-Tree 和哈希索引。数据库

B-Tree

说索引以前就不得不先说一说 B-Tree。服务器

B-Tree 是一种平衡搜索树,结构相似于普通的二叉树,区别在于每一个结点容许有更多的子结点。函数

B-Tree 结构

B-Tree,这里的图直接引用了参考中第一篇文章的图~若有侵权,烦请私信我~性能

image-20180806224627469

B+Tree 结构

B+Tree 是 B-Tree 的变种,也是一种平衡二叉树,B+Tree 如图所示,这里的图直接引用了参考中第一篇文章的图~若有侵权,烦请私信我~优化

image-20180806224734127

B-Tree 的定义

这一部分引用算导的定义方法~加密

一颗 B-Tree T 具备如下性质,.net

  • 每一个结点 x 有如下属性:
    • x.n,表示存储在结点 x 中的关键字个数
    • x.n 个关键字自己非降序存放
    • x.leaf 表示 x 是否为叶子节点
  • 内部结点包含 x.n+1 个指针指向它的孩子
  • 每一个叶子结点具备相同的深度,即树的高度h=log_t \frac{n+1}{2}
  • 每一个结点中的关键字个数有上界和下界,这里使用 B-Tree 的最小度即 t 来表示,t \ge 2
    • 除了根结点和叶子结点外,每一个结点的孩子个数为 t \leq n \leq 2t
    • 除了根结点和叶子结点外,每一个结点的关键字个数为t-1 \leq n \leq 2t-1
  • B+Tree 中非叶子结点的孩子个数和关键字个数相同

B-Tree vs B+Tree

看完图,来总结一下~设计

B-Tree,指针

  • 每一个结点上都有对应数据的存储
  • 每一个关键字出现且仅在一个结点上出现
  • 搜索可能在非叶子结点结束

B+Tree,

  • 非叶子结点不存储数据
  • 叶子结点增长了一个双向链表,因此从图中也能够看出,叶子结点上包含了全部关键字

B+Tree 的优点,

  • 因为非叶子结点不存储实际的数据,因此内存中存储更多的关键字,也就是说单次磁盘 IO 信息量会大于 B-Tree
  • 叶子结点增长了顺序链表,更适合区间查询

Why B-Tree

事实上,红黑树也能够用做索引,为何 MySQL 中使用的是 B/B+ Tree 来实现索引嘞。由于 MySQL 是基于磁盘的数据库,索引的查找过程势必会涉及到磁盘 IO,因此索引性能的两个关键点就是,

  • 磁盘 IO 的次数
  • CPU 计算速度

因此,在设计索引的时候就要尽可能减小磁盘 IO 的次数,而 MySQL 将记录按照页的方式进行管理,B-Tree 每次新建结点的时候,直接申请一个页的空间,这样就保证了一个结点物理上也存储在一个页里,而且计算机存储分配都是按页对齐,因此就实现了一个结点只须要一次 IO。在树中一次检索最多须要 h - 1 次 IO,由于根结点常驻内存,B-Tree 由于能够有不少结点个数,因此 h 很小,而结点的个数与页的大小相关,一样的数据,红黑树的 h 明显要深不少,因此一般都是用 B/B+Tree 做为索引结构。

B-Tree 的渐进时间复杂度为,这里记 N 为关键字个数,d 为内结点出度的 1/2,O(h)=O(log_dN)O(h)=O(log_dN)

红黑树的渐进时间复杂度为,O(h)

因此,为何 MySQL 使用 B/B+Tree 来实现索引也就不言而喻啦。

B-Tree 的操做

这里针对 B-Tree 的一些操做就不给出图示了,由于算导中已经给出伪代码以及很是详细的图示啦~只作一个简单总结,

  • 插入关键字:先找到关键字应该插入的叶子结点,若是结点是满的,即关键字个数为 2t-1,此时应该根据这个叶子结点中的关键字的中间的关键字进行分裂,中间的关键字会被上移到该节点的父节点中,若是父节点也是满的,那么重复上述操做,直到根结点为止,若是到根结点,那么就说明高度增长了一层
  • 删除关键字:删除关键字要比插入关键字复杂一些,由于删除的不仅是叶子结点,还能够是内结点,这时候咱们就须要考虑如何安置内结点的孩子们,而且还要保证删除后的 B-Tree 符合要求,因此删除关键字分几种状况,
    • 若是 k 在结点 x 中,且 x 为叶子结点,那么直接从 x 中删除 k 便可
    • 若是 x 中前于 k 的结点 u1 中的关键字个数为 t,那么找到 u1 中最大的关键字 key,删除 u1 中的 key,并在 x 中用 key 代替 k
    • 若是 x 中前于 k 的节点 u1 中的关键字个数小于 t,那么找到 x 中后于 k 的结点 u2,若是 u2 中的关键字个数为 t,那么找到 u2 中最小的关键字 key,删除 u2 中的 key,并在 x 中用 key 代替 k
    • 若是先后两个节点的关键字个数都是 t-1,那么合并 u1 和 u2,并在 x 中删除 k,将 x 中的指针指向新的结点
    • 若是 k 不在当前的内结点中,那么找到 k 所在的内结点后,执行上述操做便可

B-Tree 索引

以 B-Tree 为结构的索引是最多见的索引类型,好比 InnoDB 和 MyISAM 都是以 B-Tree 为索引结构的索引,事实上是以 B+ Tree 为索引结构,B-Tree 和 B+Tree 区别在于,B+ Tree 在叶子节点上增长了顺序访问指针,方便叶子节点的范围遍历。这里主要介绍一下 InnoDB 和 MyISAM 两种不一样的索引。

InnoDB

InnoDB 支持聚簇索引,聚簇索引和非聚簇索引严格来讲不是一种索引,而是一种数据存储方式,这个名字跟它自己的存储方式有关系,“聚簇“表示数据行和相邻的键值存储在一块儿,简单的说,就是叶子节点中存储的实际是真实的数据。InnoDB 经过主键汇集数据,因此一个表只能有一个聚簇索引,且必须有主键,若是没有定义主键,且不存在非空索引能够代替,InnoDB 会隐式定义一个主键做为聚簇索引。

聚簇索引的二级索引存储的不是指向行的物理位置的指针,而是行的主键值,因此若是经过二级索引查找行,须要找到二级索引的叶子结点得到对应的主键值,而后再去查找对应的行。对于 InnoDB,自适应哈希索引能够减小这样的重复工做。

MyISAM

MyISAM 支持非聚簇索引,和 InnoDB 的区别在于,叶子结点上存的是指向对应行的物理地址,也就是说索引和数据实际是分开存储的。

MyISAM 采用了前缀压缩技术,从而使得更多索引能够放入到内存中,默认只压缩字符串,可是经过参数设置也能够对整数作压缩。压缩方法为,完整保存索引块中的第一个IE之,而后将其余值和第一个值进行比较获得相同前缀的字节数和剩余的不一样后缀部分,把这部分存储起来便可。例如,索引块中的第一个值是“perform”,第二个值是“performance”,那么第二个值的前缀压缩后存储的是相似“7,ance”这样的形式。MyISAM 对行指针也采用相似的前缀压缩方式。

压缩可使索引使用更少的空间,在某种程度上性能有所提高,可是代价是某些操做可能更慢。由于每一个值的压缩前缀都依赖前面的值,因此 MyISAM 查找时没法在索引块使用二分查找,只能从头开始扫描,正序扫描速度还不错,逆序扫描的话查找某一行的操做平均都须要扫描半个索引块。对于 CPU 密集型应用,扫描须要随机查找,因此压缩索引会使得索引查找慢不少,而对于 I/O 密集型应用,对于某些查询的优化更明显。

InnoDB 使用的是行锁,因此支持事务,而 MyISAM 使用的是表锁,不支持事务。

适用范围

B-Tree 索引适用于区间查询,由于 B-Tree 存储后的叶子节点自己就是有序的,而且 B+ Tree 结构还增长了叶子节点的连续顺序指针,对于区间查询来讲就更加方便了。

哈希索引

哈希索引是基于哈希表实现的,只有精确匹配索引全部列的查询才有效。方法是,对全部的索引列计算一个 hash code,hash code 做为索引,在哈希表中保存指向每一个数据行的指针。

优势

  • 索引自己只存储 hash code,因此结构很紧凑,而且查找速度很快

限制

  • 索引中的 hash code 是顺序存储的,可是 hash code 对应的数据并非顺序的,因此没法用于排序
  • 不支持部分索引列匹配查找,由于哈希索引是使用索引列的所有内容来计算 hash code
  • 只支持等值比较,不支持范围查询
  • 若是哈希冲突严重时,必须遍历链表中全部行指针
  • 哈希冲突严重的话,索引维护操做的代价也很高

InnoDB 的自适应哈希索引

首先,请注意,自适应哈希索引对于用户来讲是无感知的,这是一个彻底自动、内部的行为,用户没法控制或者配置,可是能够关闭。

当 InnoDB 注意到某个索引值被使用的很是频繁时,它会在内存中基于 B-Tree 索引之上再建立一个哈希索引,这样 B-Tree 也能够具备哈希索引的一些优势,好比快速的哈希查找。

固然若是存储引擎不支持哈希索引,用户也能够自定义哈希索引,这样性能会比较高,缺陷是须要本身维护哈希值,若是采用这种方法,不要使用 SHA1()MD5() 做为哈希函数,由于这两个是强加密函数,设计目标是最大限度消除冲突,生成的 hash code 是一个很是长的字符串,浪费大量的空间,哈希索引中对于索引的冲突要求没有那么高。

索引的优势

  • 使用索引能够减小服务器须要扫描的数据量
  • 使用索引能够帮助服务器避免排序和临时表
  • 使用索引能够将随机 I/O 变为顺序 I/O

可是不是全部状况下,索引都是最好的解决方案,对于很是小的表来讲,大部分状况下简单的全表扫描更高效,对于中到大型表,索引就比较有效,对于特大型的表来讲,分区会更加有效。

Reference

B 树与 B+ 树

从B树、B+树、B*树谈到R 树

《算法导论》

《高性能 MySQL》

相关文章
相关标签/搜索