前言

在今天的互联网企业中,mysql是必须掌握的技能,可能维护mysql的技能都已经交给dba或者直接采用相关云服务,但是了解其中的原理还是很重要的。例如mysql中的存储引擎、事务管理、binlog日志、主从同步等等,这篇文章主要记录下对mysql的b+树的学习总结,如果对此概念已经比较了解,就可以不用在阅读了。


目录

  • 前言
  • 索引的数据结构
  • hash
  • b+树
  • b+树原理
  • 什么是二叉树?
  • 什么是b树?
  • b+树
  • mysql表设置自增长id的作用
  • 总结
  • 参考


索引的数据结构

mysql的InnoDB常用的索引数据结构有2种,hash和b+树。

hash

hash就是建立hash表,根据hash算法,建立索引键和数据的映射关系,hash可能会出现冲突,解决hash冲突有几种方式:

  1. 开放定址法: 一旦出现冲突就向后查找空位,直到找到空位为止
  2. 再hash法: 采用多个hash函数,hash出现冲突,继续下一次hash
  3. 链表法: 出现冲突时,通过链表实现共同进入该位
  4. 公共溢出区: 出现冲突时,都进入公共溢出区
    InnoDB中采用的hash结构是第3种。

优点

  • hash算法时间复杂度低,对于等值查找效率很高(但是要求查找值重复度低,如果重复度高,进入链表查询还是需要遍历)

缺点

  • 范围查找不适用
  • 无法用来做数据排序,由于hash后的数据排序可能跟原有数据排序不一致
  • 组合索引,部分索引无法实现查询,组合索引采用多键计算hash,但是如果只用前面几个索引查询,就无法使用

我们发现hash索引的局限性太强,很多场景无法适用,这也是我们实际使用中几乎没人使用该索引数据结构的原因。

b+树

我们在设计mysql数据库的表时,都会默认加个业务无关的自增长主键id,至于为啥这样可能很多人不理解,等会我们来解释。

b+树原理
什么是二叉树?

二叉搜索树有个特性,就是left-child小于都小于自己,right-child都大于自己,那么我们看下面这个树,右侧的树虽然也满足,但是其查找性能已经不是2分法而是线性,这种情况我们称之为失去了”平衡“。

mysql hash join 实现 mysql hash冲突_mysql hash join 实现

什么是b树?

b树解决了二叉树不能平衡的问题,在b树中所有的叶子节点在同一层,有以下几个特性:

  • 定义任意非叶子结点最多只有M个儿子;且M>2
  • 根结点的儿子数为[2, M];
  • 除根结点以外的非叶子结点的儿子数为[M/2, M];
  • 每个结点存放至少M/2-1(取上整)和至多M-1个关键字;
  • 非叶子结点的关键字个数=指向儿子的指针个数-1;
  • 非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
  • 非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
  • 所有叶子结点位于同一层;

b树如何实现平衡
由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

逻辑结构

mysql hash join 实现 mysql hash冲突_结点_02


特点

数据分布在所有节点中,在查找的时候一旦匹配到,就可以拿到数据。其查找的时间复杂度为: O(logN)

b+树

b+树是b树的变型,相比于b树其有以下几个不同:

  • 非叶子结点的子树指针与关键字个数相同
  • 为所有叶子结点增加一个链指针
  • 所有关键字都在叶子结点出现

相比于b树其优点

  • 非叶子节点的信息更加少,减少io开销
  • 所有数据都在叶子节点,查找性能稳定
  • 叶子节点之间有链表指针关联,更加利于遍历

鉴于以上的优势,b+树更适合做文件索引,这也是InnoDB采用的原因。

mysql表设置自增长id的作用

回到我们提出的问题,InnoDB的主键索引采用b+树,其非叶子节点中存储的是主键id,其叶子节点存储的是具体的行数据,这是聚簇索引,也就是主键索引。
非主键索引中区别在于,其叶子节点存储的不是实际的数据,而是主键的值。

那么如果不按照主键查找呢?
按照其他索引查找数据,其他索引的叶子节点并没有数据,其只有数据的主键,在查找到数据主键的时候,再去通过主键索引查找数据的数据返回。

为啥主键索引是自增长id呢?
在聚簇索引写入的时候,叶子节点时是通过通过链表关联的、且有序,顺序的主键会使磁盘写入变成顺序的,效率更高。 批量读取数据的时候效率也更高。
试想一下,如果id是非自增长的,那么每次无论是非叶子节点中插入数据会变成随机的而不是顺序的,效率自然变低。

总结

今天先说到这里,有错误的地方望指出,后续有空继续输出mysql其他方面的学习总结。