数据结构
二叉排序树(Binary Sort Tree)
规则
- 若左子树不空,则左子树上所有节点的值均小于它的根节点的值
- 若右子树不空,则右子树上所有节点的值均大于它的根节点的值
- 它的左、右子树也分别为二叉排序树(递归定义)
说明
二叉查找树查找比较方便,因为每次经过一次节点时,最多减少一半的可能。极端情况下,会出现所有节点位于同一侧的情况,直观上看就是一条直线,这种情况的查询效率比较低。因此需要对二叉树左右子树的高度作平衡化处理,这就是平衡二叉树。
平衡二叉树(Balance Binary Tree)
规则
- 左右子树的高度差的绝对值不超过1
- 左右子树都是平衡二叉树(递归定义)
说明
常见的实现方式为:红黑树,平衡二叉查找树(AVL),替罪羊树,树堆(Treap),伸展树。在这样的平衡树中进行查找,总共比较节点的次数不超过树的高度,查询效率得到提高,时间复杂度为O(logn)。
平衡多路查找树(B树或B-树)
规则
- 每个节点至多可以拥有m棵子树
- 根节点,只有至少2个节点
- 非根非叶的节点至少有Ceil(m/2)个子树(Ceil表示向上取整),例如5阶B树,每个节点至少3个子树
- 所有叶子节点位于同一层,意思是从根到叶子节点的每一条路径都有同样的长度
说明
B树查询与二叉排序树类似,从根节点依次比较每个节点,因为每个节点中关键字和左右子树都是有序的。
B+树
规则
- 有n棵子树的节点含有n个关键字,每个关键字不保存数据,只用来索引,所有数据保存在叶子节点
- 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接
- 非叶子节点看成是索引部分,结点中仅含其子树(根节点)中的最大(或最小)关键字
说明
B+树查找过程与B树类似,只不过查找时,如果在非叶子节点上的关键字等于给定值,并不终止,而是继续沿着指针直到叶子节点。因此对于B+树,不管查找成功或失败,每次查找都是走了一条从根到叶子节点的路径。
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构(双向循环链表)。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
实现
MyISAM索引实现
MyISAM引擎使用B+Tree作为索引结构,叶结点的data域存放的是数据记录的地址。
主索引(主键)
辅助索引
在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复。
说明
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶结点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
主索引(主键)
辅助索引
说明
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为103)。也就是说一个深度为3的B+Tree索引可以维护103 * 10^3 * 10^3 = 10亿 条记录。实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层。mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。
InnoDB 要求表必须有主键(MyISAM 可以没有),如果没有显式指定,则 MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,类型为长整形。
同时,请尽量在 InnoDB 上采用自增字段做表的主键。因为 InnoDB 数据文件本身是一棵B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持 B+Tree 的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。
聚簇索引
InnoDB 使用的是聚簇索引,将主键组织到一棵B+树中, 而行数据就储存在叶子节点上, 若使用"where id = 14"这样的条件查找主键,则按照 B+树的检索算法即可查找到对应的叶节点,之后获得行数据。 若对 Name 列进行条件搜索,则需要两个步骤:
- 第一步、在辅助索引 B+树中检索 Name,到达其叶子节点获取对应的主键。
- 第二步、使用主键在主索引B+树种再执行一次 B+树检索操作,最终到达叶子节点即可获取整行数据。
非聚簇索引
MyISM 使用的是非聚簇索引, 非聚簇索引的两棵 B+树看上去没什么不同, 节点的结构完全一致只是存储的内容不同而已, 主键索引 B+树的节点存储了主键, 辅助键索引B+树存储了辅助键。 表数据存储在独立的地方, 这两颗 B+树的叶子节点都使用一个地址指向真正的表数据, 对于表数据来说, 这两个键没有任何差别。 由于索引树是独立的, 通过辅助键检索无需访问主键的索引树。