索引的数据结构:B+Tree

B树与B+树

索引的数据结构:B+Tree_第1张图片

B+Tree:

  1. 多叉树
  2. 叶子节点(处于最底层的节点)、内节点(非叶子节点)
  3. 一个节点可以有多个K-V对,从左到右,生序
  4. 内节点不存储Key对应的data。data只会存储在叶子节点K-V对中
  5. 内节点中的Key会出现重复
  6. 所有的Key都会不重复的出现在叶子节点中
  7. 相邻的节点有指针相互连接(双向)

B-Tree:

  1. 多叉树
  2. 叶子节点(处于最底层的节点)、内节点(非叶子节点)
  3. 一个节点可以有多个K-V对,从左到右,生序
  4. data存储在每个K-V对中
  5. 一个Key只会出现在一个节点中

B+Tree的优势:

  1. B+树的内节点无data,一个节点可以存储更多的K-V对。在构造树时,需要的内节点会更少,那么树的层级也会越低。查询一条数据时,1. 扫描的层级低,扫描过的节点更少,2. 过程更加稳定。
  2. 每一层的节点与相邻节点有指针相互连接(双向),数据可顺序逆序、跨页查找,对于范围查询更友好

B+Tree的缺点:内节点的K-V对会冗余,多占用空间。

InnoDB中的B+Tree

索引的数据结构:B+Tree_第2张图片

min节点:所处的页面位置的不同,最小值节点含义也不同

  1. 叶子结点中:代表一个数据页的最小边界值
  2. 内节点中:value是下层子页面的页号
  3. 每层中最左侧的节点:当前层key值的最小边界

页存储分析与计算

页的默认大小为16KB
页头大小:120B
页尾大小:8B
Key:8B (假设使用Bigint)
页号大小:8B

计算一个页能存储多少K-V?
16KB = 16 * 1024B =  (120+8)B + n * (8+8)B
n = 1016 - 6(页尾浮动耗损) ≈ 1010个

假设叶子节点正好可存储两条完整数据,即表的每个字段的数据都是完全填充的,一行数据最大为8K,计算4层的B+树可以存储多少条数据?

1层:1010个Key
2层:1010 * 1010 = 1,020,100个Key
3层:1010 * 1010 * 1010 = 1,030,301,000 ≈ 10亿个Key
4层:叶子节点,存储行数据,≈ 20亿条数据

若是首次查询,页数据没有没加载到内存,从20亿数据中查询一条数据,仅需要4次IO读取。若读取数据的页已被加载到内存,直接就可以从内存中读取到数据。

页分裂

根页分裂

索引的数据结构:B+Tree_第3张图片

  • 分裂2号、3号页
  • 根页的数据完全cut到2号页
  • 将2号页的一半cut到3号页
  • Key:50 添加到2号页中
  • 2号页与3号页相互指向
  • 1号页中添加2号页与3号页的项
非根页分裂

索引的数据结构:B+Tree_第4张图片

  • 分裂4号页
  • 3号数据的一半cut到4号页
  • Key: 90添加到4号页中
  • 3号页与4号页相互指向
  • 1号页中添加4号页的项

数据删除

索引的数据结构:B+Tree_第5张图片

  1. 前继节点有需要被删除的值,递归删除。删除后,若当前节点内仍存有K值,则将当前节点最小的K值递归更新前继节点,并指向下一层的节点(节点 = 页)
  2. 前继节点没有需要被删除的值,直接在当前节点中删除即可

页合并

索引的数据结构:B+Tree_第6张图片

  1. 页合并时,会将两个页合并为一个页,并将另一个页摘除。
  2. 根据页合并阈值参数(InnoDb: Merge_Threshold),执行页合并操作。默认为50,即当相邻的两个页中的K-V对量小于页可容量的50%时,触发页合并。
  3. 阈值如果过大,会频繁加排他锁执行页合并,反之会导致碎片化。

页回收

删除页、页分裂、插入数据后回滚、插入数据失败(如批量插入时,Kill了进程),都会导致表的碎片化。那么如何优化呢?一种方式:optimize table blog_info。原理是重建一颗B+树,将数据重新放入其中即可。不过这个过程很重,可以定期优化,不要频繁这么操作。

你可能感兴趣的:(数据库,mysql,b+树)