《Java数据结构》Java B+树结构

本文介绍了B+树的原理及其在MySQL查询优化中的作用。B+树是一种高效的索引结构,允许数据重复,三层B+树可以存储大量数据。文章通过实例展示了B+树的查找效率,并讨论了其在插入和删除操作中的复杂性。同时,文章提供了一个简单的Java实现B+树的示例,有助于深入理解B+树。
摘要由CSDN通过智能技术生成

前言

在学习MySQL的时候遇到了B+树,MySQL通过B+树来提升SQL语句的查询效率。接下来我们就来分析一下B+树的原理和写一个demo模拟B+树的实现。

B+树原理

1. 什么是B+树

B+树是一种B树的变形,看看B+树结构

在这里插入图片描述

根据图我们可以看出B+树存在重复元素的存储。物理存储空间要比一般的树暂用的多,不过多的空间并不多。

上图是一个简图,实际一个三层B+树可以存储很多数据,我们按照MySQL的逻辑计算一下一个3层B+树可以存多少数据,

MySQL 中一页是16KB, 一个主键bigint字段暂用空间(8+6)个字节,8是bigint的大小,6是指针大小,假设每一行数据大小为1K。公式如下:

可以存储的数据 = (161024/(8+6)) * (161024/(8+6)) * 16K/1K = 21902400。 两千多万条数据。

2. B+树的作用

在实现B+树之前,我们先看看这个B+树结构的好处。

上面已经将一个3层B+树就可以存下如此多的数据,那么这个结果我们该如何找到我们需要的数据呢。

假设我们找到数字10.

第一种方式是:从左往右找

在这里插入图片描述

第10次才能找到数据,如果数据真的是千万,那这查询需要千万次计算,性能消耗太大。 这种找法一般适合找的数据很多,但是整体数据不多的情况下使用,

例如你找10条记录中需要找9条记录。

第二种方式是:从根节点开始查找

在这里插入图片描述

从下到下找,1,7,13 找三次找到他们子节点7,9,11,然后再找三次找到子节点9,10 ,再找一次就找到了,找了6次。

如果我们将数据扩大了2千万,我们第一层需要找最多 (16*1024/(8+6)) = 1170 次,第二层也是1170次,第三层还是1170次,最多最多我们只需要3千多次就冲2千万的数据中找到

我们需要的数据。极大的提升了查询效率。

查询效率提升的同时插入效率就会下降。

我们假设在上面这个树的基础上,插入一个数据19,每一层的数据都需要变动,如果我们一个空间只能存3个数据,那树还需要加高一层。插入变得复杂了一些。

同理,删除一样。

总结一下:B+树的特点

1、非叶子节点的子树指针与关键字个数相同;
2、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复);
3、为所有叶子节点增加一个链指针;
4、所有关键字都在叶子节点出现(稠密索引). (且链表中的关键字恰好是有序的);
5、非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层;

实现

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@SuppressWarnings("all")
public class BPlusNode<K extends Comparable<K>, V> {

    // 是否为叶子节点
    protected boolean isLeaf;

    // 是否存在根节点
    protected boolean isRoot;

    // 父节点
    protected BPlusNode<K, V> parent;

    // 叶节点的前节点
    protected BPlusNode<K, V> previous;

    // 叶节点的后节点
    protected BPlusNode<K, V> next;

    // 节点的关键字列表
    protected List<Map.Entry<K, V>> entries;

    // 子节点列表
    protected List<BPlusNode<K, V>> children;

    public BPlusNode(boolean isLeaf) {
        this.isLeaf = isLeaf;
        entries = new ArrayList();

        if (!isLeaf) {
            children = new ArrayList();
        }
    }

    public BPlusNode(boolean isLeaf, boolean isRoot) {
        this(isLeaf);
        this.isRoot = isRoot;
    }

    public V get(K key) {
        //如果是叶子节点
        if (isLeaf) {
            int low = 0, high = entries.size() - 1, mid;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                if (comp == 0) {
                    return entries.get(mid).getValue();
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            //未找到所要查询的对象
            return null;
        }
        //如果不是叶子节点
        //如果key小于节点最左边的key,沿第一个子节点继续搜索
        if (key.compareTo(entries.get(0).getKey()) < 0) {
            return children.get(0).get(key);
            //如果key大于等于节点最右边的key,沿最后一个子节点继续搜索
        } else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
            return children.get(children.size() - 1).get(key);
            //否则沿比key大的前一个子节点继续搜索
        } else {
            int low = 0, high = entries.size() - 1, mid = 0;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                if (comp == 0) {
                    return children.get(mid + 1).get(key);
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            return children.get(low).get(key);
        }
    }//需要获取资料的朋友请加Q君样:290194256*


    public void insertOrUpdate(K key, V value, BPlusTree<K, V> tree) {
        //如果是叶子节点
        if (isLeaf) {
            //不需要分裂,直接插入或更新
            if (contains(key) != -1 || entries.size() < tree.getOrder()) {
                insertOrUpdate(key, value);
                if (tree.getHeight() == 0) {
                    tree.setHeight(1);
                }
                return;
            }
            //需要分裂
            //分裂成左右两个节点
            BPlusNode<K, V> left = new BPlusNode<K, V>(true);
            BPlusNode<K, V> right = new BPlusNode<K, V>(true);
            //设置链接
            if (previous != null) {
                previous.next = left;
                left.previous = previous;
            }
            if (next != null) {
                next.previous = right;
                right.next = next;
            }
            if (previous == null) {
                tree.setHead(left);
            }

            left.next = right;
            right.previous = left;
            previous = null;
            next = null;

            //复制原节点关键字到分裂出来的新节点
            copy2Nodes(key, value, left, right, tree);

            //如果不是根节点
            if (parent != null) {
                //调整父子节点关系
                int index = parent.children.indexOf(this);
                parent.children.remove(this);
                left.parent = parent;
                right.parent = parent;
                parent.children.add(index, left);
                parent.children.add(index + 1, right);
                parent.entries.add(index, right.entries.get(0));
                entries = null; //删除当前节点的关键字信息
                children = null; //删除当前节点的孩子节点引用

                //父节点插入或更新关键字
                parent.updateInsert(tree);
                parent = null; //删除当前节点的父节点引用
                //如果是根节点
            } else {
            
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值