手写B+树

news2024/11/19 7:47:30

1.特点

https://blog.csdn.net/weixin_57128596/article/details/127030901?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167176442416800188598723%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=167176442416800188598723&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-2-127030901-null-null.blog_rank_default&utm_term=B%2B&spm=1018.2226.3001.4450
1.整体有序
2.一个节点下面多个元素
3.非叶子节点没有指向当前数据的指针,叶子节点有
4.节点下的元素是有序的
5.叶子节点包含上面的所有数据,叶子节点之间是一个链表,不需要返回根节点再搜索其他节点**(核心 )**

在这里插入图片描述

2. 抛出问题:为什么数据库插入数据会默认给你排好序

下图所示:插入了几条数据进入mysql,会根据主键进行排序,用主键效率会高一点,不然一下中间一下下面的,效率肯定受影响,自增往后面叠就行了
在这里插入图片描述

查询

在查询的时候,原先查询数据就像翻书一样一页一页翻,而我们这里你找到了就不会继续向下遍历了

更重要的是设置了目录:

会根据我们设置的主键确定目录,我们只需要判断有没有这个目录就能确定这个数据在不在这里面了,效率非常高,比如查询a=3,目录由主键确定1和4,那么a=3就会从主键为1的地方目录进行遍历寻找,如果遍历完没有就没有**(剩下了很多的遍历)**

在这里插入图片描述

一页只要16kb满了怎么办

如果一页存储插入的数据有限,那么更多的数据就会到第二页上面
当我们进行搜寻的时候就会先遍历第一页,如果第一页没有再在第二页中搜寻——>(页数很多的情况搜索很难,比如数据在100页但是我还遍历了前99页的目录)
在这里插入图片描述

页目录优化

在创建一个目录,将每一页页目录中最小的主键放到这个目录里面,当我们进行搜索的时候可以先根据这个目录确定数据所在数据页,然后进而确定哪个位置
比如下面的你找主键id为3,就会根据主键值找到具体的数据页,然后再根据数据的页目录找到具体的用户数据
其实我们这个页目录就很像索引的建立

在这里插入图片描述

2.插入操作

https://zhuanlan.zhihu.com/p/149287061#:~:text=B%2B%E6%A0%91%E7%9A%84%E6%8F%92%E5%85%A5%E6%93%8D%E4%BD%9C%201%20%E6%8F%92%E5%85%A5%E7%9A%84%E6%93%8D%E4%BD%9C%E5%85%A8%E9%83%A8%E9%83%BD%E5%9C%A8%E5%8F%B6%E5%AD%90%E7%BB%93%E7%82%B9%E4%B8%8A%E8%BF%9B%E8%A1%8C%EF%BC%8C%E4%B8%94%E4%B8%8D%E8%83%BD%E7%A0%B4%E5%9D%8F%E5%85%B3%E9%94%AE%E5%AD%97%E8%87%AA%E5%B0%8F%E8%80%8C%E5%A4%A7%E7%9A%84%E9%A1%BA%E5%BA%8F%EF%BC%9B,2%20%E7%94%B1%E4%BA%8E%20B%2B%E6%A0%91%E4%B8%AD%E5%90%84%E7%BB%93%E7%82%B9%E4%B8%AD%E5%AD%98%E5%82%A8%E7%9A%84%E5%85%B3%E9%94%AE%E5%AD%97%E7%9A%84%E4%B8%AA%E6%95%B0%E6%9C%89%E6%98%8E%E7%A1%AE%E7%9A%84%E8%8C%83%E5%9B%B4%EF%BC%8C%E5%81%9A%E6%8F%92%E5%85%A5%E6%93%8D%E4%BD%9C%E5%8F%AF%E8%83%BD%E4%BC%9A%E5%87%BA%E7%8E%B0%E7%BB%93%E7%82%B9%E4%B8%AD%E5%85%B3%E9%94%AE%E5%AD%97%E4%B8%AA%E6%95%B0%E8%B6%85%E8%BF%87%E9%98%B6%E6%95%B0%E7%9A%84%E6%83%85%E5%86%B5%EF%BC%8C%E6%AD%A4%E6%97%B6%E9%9C%80%E8%A6%81%E5%B0%86%E8%AF%A5%E7%BB%93%E7%82%B9%E8%BF%9B%E8%A1%8C%20%E2%80%9C%E5%88%86%E8%A3%82%E2%80%9D%EF%BC%9B
在B+树中插入关键字时,需要注意以下几点:

1.插入的操作全部都在叶子结点上进行,且不能破坏关键字自小而大的顺序;
2.由于 B+树中各结点中存储的关键字的个数有明确的范围,做插入操作可能会出现结点中关键字个数超过阶数的情况,此时需要将该结点进行 “分裂”
我们依旧以之前介绍查找操作时使用的图对插入操作进行说明,需要注意的是,B+树的阶数 M = 3 ,且 ⌈M/2⌉ = 2(取上限) 、⌊M/2⌋ = 1(取下限) :

在这里插入图片描述
B+树中做插入关键字的操作,有以下 3 种情况:

1、 若被插入关键字所在的结点,其含有关键字数目小于阶数 M,则直接插入;

比如插入关键字 12 ,插入关键字所在的结点的 [10,15] 包含两个关键字,小于 M ,则直接插入关键字 12

2、 若被插入关键字所在的结点,其含有关键字数目等于阶数 M;
则需要将该结点分裂为两个结点,一个结点包含 ⌊M/2⌋ ,另一个结点包含 ⌈M/2⌉ 。同时,将⌈M/2⌉的关键字上移至其双亲结点。假设其双亲结点中包含的关键字个数小于 M,则插入操作完成。

插入关键字 95 ,插入关键字所在结点 [85、91、97] 包含 3 个关键字,等于阶数 M ,则将 [85、91、97] 分裂为两个结点 [85、91] 和结点 [97] , 关键字 95 插入到结点 [95、97] 中,并将关键字 91 上移至其双亲结点中,发现其双亲结点 [72、97] 中包含的关键字的个数 2 小于阶数 M ,插入操作完成

3、在第 2 情况中,如果上移操作导致其双亲结点中关键字个数大于 M,则应继续分裂其双亲结点。

插入关键字 40 ,按照第 2 种情况将结点分裂,并将关键字 37 上移到父结点,发现父结点 [15、37、44、59] 包含的关键字的个数大于 M ,所以将结点 [15、37、44、59] 分裂为两个结点 [15、37] 和结点 [44、59] ,并将关键字 37 上移到父结点中 [37、59、97] . 父结点包含关键字个数没有超过 M ,插入结束。

3.时间复杂度:

B+树插入删除时间复杂度为0(1)
搜索的时间复杂度为0(logn)

4.代码

package com.wyh.bootmath_java.Treap;

/**
 * 实现B+树
 *
 * @param <T> 指定值类型
 * @param <V> 使用泛型,指定索引类型,并且指定必须继承Comparable
 */
public class BPlusTree <T, V extends Comparable<V>>{
    //B+树的阶
    private Integer bTreeOrder;
    //B+树的非叶子节点最小拥有的子节点数量(同时也是键的最小数量)
    //private Integer minNUmber;
    //B+树的非叶子节点最大拥有的节点数量(同时也是键的最大数量)
    private Integer maxNumber;

    private Node<T, V> root;

    private LeafNode<T, V> left;

    //无参构造方法,默认阶为3
    public BPlusTree(){
        this(3);
    }

    //有参构造方法,可以设定B+树的阶
    public BPlusTree(Integer bTreeOrder){
        this.bTreeOrder = bTreeOrder;
        //this.minNUmber = (int) Math.ceil(1.0 * bTreeOrder / 2.0);
        //因为插入节点过程中可能出现超过上限的情况,所以这里要加1
        this.maxNumber = bTreeOrder + 1;
        this.root = new LeafNode<T, V>();
        this.left = null;
    }

    //查询
    public T find(V key){
        T t = this.root.find(key);
        if(t == null){
            System.out.println("不存在");
        }
        return t;
    }

    //插入
    public void insert(T value, V key){
        if(key == null)
            return;
        Node<T, V> t = this.root.insert(value, key);
        if(t != null)
            this.root = t;
        this.left = (LeafNode<T, V>)this.root.refreshLeft();

//        System.out.println("插入完成,当前根节点为:");
//        for(int j = 0; j < this.root.number; j++) {
//            System.out.print((V) this.root.keys[j] + " ");
//        }
//        System.out.println();
    }


    /**
     * 节点父类,因为在B+树中,非叶子节点不用存储具体的数据,只需要把索引作为键就可以了
     * 所以叶子节点和非叶子节点的类不太一样,但是又会公用一些方法,所以用Node类作为父类,
     * 而且因为要互相调用一些公有方法,所以使用抽象类
     *
     * @param <T> 同BPlusTree
     * @param <V>
     */
    abstract class Node<T, V extends Comparable<V>>{
        //父节点
        protected Node<T, V> parent;
        //子节点
        protected Node<T, V>[] childs;
        //键(子节点)数量
        protected Integer number;
        //键
        protected Object keys[];

        //构造方法
        public Node(){
            this.keys = new Object[maxNumber];
            this.childs = new Node[maxNumber];
            this.number = 0;
            this.parent = null;
        }

        //查找
        abstract T find(V key);

        //插入
        abstract Node<T, V> insert(T value, V key);

        abstract LeafNode<T, V> refreshLeft();
    }


    /**
     * 非叶节点类
     * @param <T>
     * @param <V>
     */

    class BPlusNode <T, V extends Comparable<V>> extends Node<T, V>{

        public BPlusNode() {
            super();
        }

        /**
         * 递归查找,这里只是为了确定值究竟在哪一块,真正的查找到叶子节点才会查
         * @param key
         * @return
         */
        @Override
        T find(V key) {
            int i = 0;
            while(i < this.number){
                if(key.compareTo((V) this.keys[i]) <= 0)
                    break;
                i++;
            }
            if(this.number == i)
                return null;
            return this.childs[i].find(key);
        }

        /**
         * 递归插入,先把值插入到对应的叶子节点,最终讲调用叶子节点的插入类
         * @param value
         * @param key
         */
        @Override
        Node<T, V> insert(T value, V key) {
            int i = 0;
            while(i < this.number){
                if(key.compareTo((V) this.keys[i]) < 0)
                    break;
                i++;
            }
            if(key.compareTo((V) this.keys[this.number - 1]) >= 0) {
                i--;
//                if(this.childs[i].number + 1 <= bTreeOrder) {
//                    this.keys[this.number - 1] = key;
//                }
            }

//            System.out.println("非叶子节点查找key: " + this.keys[i]);

            return this.childs[i].insert(value, key);
        }

        @Override
        LeafNode<T, V> refreshLeft() {
            return this.childs[0].refreshLeft();
        }

        /**
         * 当叶子节点插入成功完成分解时,递归地向父节点插入新的节点以保持平衡
         * @param node1
         * @param node2
         * @param key
         */
        Node<T, V> insertNode(Node<T, V> node1, Node<T, V> node2, V key){

//            System.out.println("非叶子节点,插入key: " + node1.keys[node1.number - 1] + " " + node2.keys[node2.number - 1]);

            V oldKey = null;
            if(this.number > 0)
                oldKey = (V) this.keys[this.number - 1];
            //如果原有key为null,说明这个非节点是空的,直接放入两个节点即可
            if(key == null || this.number <= 0){
//                System.out.println("非叶子节点,插入key: " + node1.keys[node1.number - 1] + " " + node2.keys[node2.number - 1] + "直接插入");
                this.keys[0] = node1.keys[node1.number - 1];
                this.keys[1] = node2.keys[node2.number - 1];
                this.childs[0] = node1;
                this.childs[1] = node2;
                this.number += 2;
                return this;
            }
            //原有节点不为空,则应该先寻找原有节点的位置,然后将新的节点插入到原有节点中
            int i = 0;
            while(key.compareTo((V)this.keys[i]) != 0){
                i++;
            }
            //左边节点的最大值可以直接插入,右边的要挪一挪再进行插入
            this.keys[i] = node1.keys[node1.number - 1];
            this.childs[i] = node1;

            Object tempKeys[] = new Object[maxNumber];
            Object tempChilds[] = new Node[maxNumber];

            System.arraycopy(this.keys, 0, tempKeys, 0, i + 1);
            System.arraycopy(this.childs, 0, tempChilds, 0, i + 1);
            System.arraycopy(this.keys, i + 1, tempKeys, i + 2, this.number - i - 1);
            System.arraycopy(this.childs, i + 1, tempChilds, i + 2, this.number - i - 1);
            tempKeys[i + 1] = node2.keys[node2.number - 1];
            tempChilds[i + 1] = node2;

            this.number++;

            //判断是否需要拆分
            //如果不需要拆分,把数组复制回去,直接返回
            if(this.number <= bTreeOrder){
                System.arraycopy(tempKeys, 0, this.keys, 0, this.number);
                System.arraycopy(tempChilds, 0, this.childs, 0, this.number);

//                System.out.println("非叶子节点,插入key: " + node1.keys[node1.number - 1] + " " + node2.keys[node2.number - 1] + ", 不需要拆分");

                return null;
            }

//            System.out.println("非叶子节点,插入key: " + node1.keys[node1.number - 1] + " " + node2.keys[node2.number - 1] + ",需要拆分");

            //如果需要拆分,和拆叶子节点时类似,从中间拆开
            Integer middle = this.number / 2;

            //新建非叶子节点,作为拆分的右半部分
            BPlusNode<T, V> tempNode = new BPlusNode<T, V>();
            //非叶节点拆分后应该将其子节点的父节点指针更新为正确的指针
            tempNode.number = this.number - middle;
            tempNode.parent = this.parent;
            //如果父节点为空,则新建一个非叶子节点作为父节点,并且让拆分成功的两个非叶子节点的指针指向父节点
            if(this.parent == null) {

//                System.out.println("非叶子节点,插入key: " + node1.keys[node1.number - 1] + " " + node2.keys[node2.number - 1] + ",新建父节点");

                BPlusNode<T, V> tempBPlusNode = new BPlusNode<>();
                tempNode.parent = tempBPlusNode;
                this.parent = tempBPlusNode;
                oldKey = null;
            }
            System.arraycopy(tempKeys, middle, tempNode.keys, 0, tempNode.number);
            System.arraycopy(tempChilds, middle, tempNode.childs, 0, tempNode.number);
            for(int j = 0; j < tempNode.number; j++){
                tempNode.childs[j].parent = tempNode;
            }
            //让原有非叶子节点作为左边节点
            this.number = middle;
            this.keys = new Object[maxNumber];
            this.childs = new Node[maxNumber];
            System.arraycopy(tempKeys, 0, this.keys, 0, middle);
            System.arraycopy(tempChilds, 0, this.childs, 0, middle);

            //叶子节点拆分成功后,需要把新生成的节点插入父节点
            BPlusNode<T, V> parentNode = (BPlusNode<T, V>)this.parent;
            return parentNode.insertNode(this, tempNode, oldKey);
        }

    }

    /**
     * 叶节点类
     * @param <T>
     * @param <V>
     */
    class LeafNode <T, V extends Comparable<V>> extends Node<T, V> {

        protected Object values[];
        protected LeafNode left;
        protected LeafNode right;

        public LeafNode(){
            super();
            this.values = new Object[maxNumber];
            this.left = null;
            this.right = null;
        }

        /**
         * 进行查找,经典二分查找,不多加注释
         * @param key
         * @return
         */
        @Override
        T find(V key) {
            if(this.number <=0)
                return null;

//            System.out.println("叶子节点查找");

            Integer left = 0;
            Integer right = this.number;

            Integer middle = (left + right) / 2;

            while(left < right){
                V middleKey = (V) this.keys[middle];
                if(key.compareTo(middleKey) == 0)
                    return (T) this.values[middle];
                else if(key.compareTo(middleKey) < 0)
                    right = middle;
                else
                    left = middle;
                middle = (left + right) / 2;
            }
            return null;
        }

        /**
         *
         * @param value
         * @param key
         */
        @Override
        Node<T, V> insert(T value, V key) {

//            System.out.println("叶子节点,插入key: " + key);

            //保存原始存在父节点的key值
            V oldKey = null;
            if(this.number > 0)
                oldKey = (V) this.keys[this.number - 1];
            //先插入数据
            int i = 0;
            while(i < this.number){
                if(key.compareTo((V) this.keys[i]) < 0)
                    break;
                    i++;
            }

            //复制数组,完成添加
            Object tempKeys[] = new Object[maxNumber];
            Object tempValues[] = new Object[maxNumber];
            System.arraycopy(this.keys, 0, tempKeys, 0, i);
            System.arraycopy(this.values, 0, tempValues, 0, i);
            System.arraycopy(this.keys, i, tempKeys, i + 1, this.number - i);
            System.arraycopy(this.values, i, tempValues, i + 1, this.number - i);
            tempKeys[i] = key;
            tempValues[i] = value;

            this.number++;

//            System.out.println("插入完成,当前节点key为:");
//            for(int j = 0; j < this.number; j++)
//                System.out.print(tempKeys[j] + " ");
//            System.out.println();

            //判断是否需要拆分
            //如果不需要拆分完成复制后直接返回
            if(this.number <= bTreeOrder){
                System.arraycopy(tempKeys, 0, this.keys, 0, this.number);
                System.arraycopy(tempValues, 0, this.values, 0, this.number);

                //有可能虽然没有节点分裂,但是实际上插入的值大于了原来的最大值,所以所有父节点的边界值都要进行更新
                Node node = this;
                while (node.parent != null){
                    V tempkey = (V)node.keys[node.number - 1];
                    if(tempkey.compareTo((V)node.parent.keys[node.parent.number - 1]) > 0){
                        node.parent.keys[node.parent.number - 1] = tempkey;
                        node = node.parent;
                    }
                    else {
                    	break;
                    }
                }
//                System.out.println("叶子节点,插入key: " + key + ",不需要拆分");

                return null;
            }

//            System.out.println("叶子节点,插入key: " + key + ",需要拆分");

            //如果需要拆分,则从中间把节点拆分差不多的两部分
            Integer middle = this.number / 2;

            //新建叶子节点,作为拆分的右半部分
            LeafNode<T, V> tempNode = new LeafNode<T, V>();
            tempNode.number = this.number - middle;
            tempNode.parent = this.parent;
            //如果父节点为空,则新建一个非叶子节点作为父节点,并且让拆分成功的两个叶子节点的指针指向父节点
            if(this.parent == null) {

//                System.out.println("叶子节点,插入key: " + key + ",父节点为空 新建父节点");

                BPlusNode<T, V> tempBPlusNode = new BPlusNode<>();
                tempNode.parent = tempBPlusNode;
                this.parent = tempBPlusNode;
                oldKey = null;
            }
            System.arraycopy(tempKeys, middle, tempNode.keys, 0, tempNode.number);
            System.arraycopy(tempValues, middle, tempNode.values, 0, tempNode.number);

            //让原有叶子节点作为拆分的左半部分
            this.number = middle;
            this.keys = new Object[maxNumber];
            this.values = new Object[maxNumber];
            System.arraycopy(tempKeys, 0, this.keys, 0, middle);
            System.arraycopy(tempValues, 0, this.values, 0, middle);

            this.right = tempNode;
            tempNode.left = this;

            //叶子节点拆分成功后,需要把新生成的节点插入父节点
            BPlusNode<T, V> parentNode = (BPlusNode<T, V>)this.parent;
            return parentNode.insertNode(this, tempNode, oldKey);
        }

        @Override
        LeafNode<T, V> refreshLeft() {
            if(this.number <= 0)
                return null;
            return this;
        }
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/110907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot mybatis大学生校园宿舍管理系统源码含文档

摘 要&#xff1a;宿舍是大学生学习与生活的主要场所之一&#xff0c;宿舍管理是高校学工管理事务中 尤为重要的一项。随着我国高校招生规模的进一步扩大&#xff0c;学生总体人数的不断增加&#xff0c; 宿舍管理工作变得愈加沉重和琐碎&#xff0c;学生宿舍信息的采集、汇总…

Classifier Guided Diffusion

前言 上次已经学习了open AI的 DDPM(DDPM原理与代码剖析)和 IDDPM(IDDPM原理和代码剖析), 以及 斯坦福的 DDIM DDIM原理及代码(Denoising diffusion implicit models). 这次来看openAI的另一个作品 Diffusion Models Beat GANs on Image Synthesis github: https://github.com…

操作系统(4)页面替换策略算法模拟实现

1. 效果展示 2. 程序流程图 &#xff08;1&#xff09; 程序总流程 &#xff08;2&#xff09; FIFO策略 &#xff08;3&#xff09; LRU策略 &#xff08;4&#xff09; OPT策略 3. 数据结构设计 //页面数据结构struct PageInfo {int id;int visit;};PageInfo* Block; // 物…

我国电力物流行业发展现存问题及解决策略分析 企业应加强信息化建设

电力物流是电力发、输、配、售流程的一部分&#xff0c;是为了满足终端用户需求&#xff0c;对电力从发电到终端用户的高效率、高效益的流动以及流程各环节相互提供的服务和相关信息在此过程中的正、反向流动所进行的计划、实施与控制过程。 根据观研报告网发布的《中国电力物流…

nginx反向代理ssl配置

nginx反向代理是各大应用环境最重要的环节&#xff0c;今天我们来说说如何配置反代理SSL这书&#xff0c;首先配置准备好&#xff1a;nginx环境、SSL证书、确定需要设置的端口防火墙开放&#xff08;尤其是云服务器需要单独设置&#xff0c;windows防火墙单独设置&#xff09;。…

电巢:上海半导体投资浪潮的前奏、高潮与转折

朱旭东是上海半导体投资界的开创性人物&#xff0c;他带领浦科投资投出了中微半导体、盛美半导体等当下的「国之重器」&#xff0c;并控股了万业企业、上工申贝等A股上市公司。随后又在国内率先发起设立并管理了上海半导体装备材料产业投资基金&#xff0c;推动旗下上市公司万业…

前端开发--JavaScript Bom和Dom

JavaScript Bom和Dom这一篇就够啦JavaScript Bom和Dom1、Web API 基本认知2、DOM -- 基础2.1 DOM简介2.2 获取元素2.3 事件基础2.4 操作元素2.5 节点操作2.6 DOM重点核心3、DOM -- 事件高级3.1 注册事件(绑定事件)3.2 删除事件(解绑事件)3.3 DOM事件流3.4 事件对象3.5 事件委托…

如何理解UML2.5.1(05篇)

标记subsets和redefines可以说是UML2.5.1中最重要的一对标记。标记subsets相对要好理解一些&#xff0c;但是redefines的含义就没有那么直白。先看一下目前已知的subsets和redefines的效果&#xff1a; 假定图一中A1和B1的关联两端的标记都是subsets&#xff0…

Torch计算方法

Torch 中的计算方法与 Numpy 的计算方法很类似&#xff1b;Torch中带 “下划线 ” 的操作&#xff0c;都是in-place的。 求和&#xff1a; torch.sum() 对输入的 tensor 数据的某一维度求和&#xff1b; &#xff11;&#xff0e;torch.sum(input, dtypeNone) &#xff12;&…

硬盘 / 硬盘控制器主要端口寄存器 / Controller Register

文章目录IDE 与 SATA硬盘分区表结构硬盘控制器主要端口寄存器data 寄存器Error && FeaturesErrorFeaturesSector countLBA low | mid | highdevice 寄存器StatusCommandIDE 与 SATA 很久以前&#xff0c;硬盘控制器和硬盘是分开的&#xff0c;后面开发了一个新接口&am…

【观察】新华三:智慧计算“再进化”,算力创新“再升级”

今天&#xff0c;算力就是生产力已成为业界共识&#xff0c;特别是算力作为数字经济时代的关键生产力要素&#xff0c;更成为了挖掘数据要素价值&#xff0c;推动数字经济发展的核心支撑力和驱动力。在此过程中&#xff0c;由算力驱动的数字经济除了以信息产业这一独立的经济形…

Linux进程间通信(二):命名管道的使用和原理

文章目录一、前言二、命名管道的使用一、前言 我们上篇博客里谈到&#xff0c;进程间通信的本质在于如何让两个进程看到同一份资源。匿名管道的核心思想就是让子进程继承父进程&#xff0c;从而让父子进程看到同一份管道文件&#xff0c;但这也使通信仅仅局限在具有血缘关系的进…

比Sqoop功能更加强大开源数据同步工具DataX实战

文章目录概述定义与Sqoop对比框架设计支持插件核心架构核心优势部署基础环境安装从stream读取数据并打印到控制台读取MySQL写入HDFS读取HDFS写入MySQL执行流程概述 定义 DataX 官网地址 https://maxwells-daemon.io/ DataX GitHub源码地址 https://github.com/alibaba/DataX D…

SaaS是什么?企业采购SaaS有什么好处?

简单的来讲讲我们对SaaS的理解吧。过去通常来说&#xff0c;我们采购企业使用的产品&#xff0c;通常有比如传统的软件包下载、按照自己的需求找开发商定制、如果有自研能力的团队可以自己去开发。但是这样就比如带来很多问题&#xff1a; 比如业务规则更新了怎么办&#xff1…

Protobuf 了解

Protocol Buffers 是一种结构数据序列化方法&#xff0c;可以将C中定义的存储类的内容与二进制序列串相互转换&#xff0c;主要用于数据传输或数据存储&#xff0c;可类比XML、JSON&#xff0c;而XML、JSON基于文本格式&#xff0c;protobuf是二进制格式&#xff0c;所以比XML、…

【论文阅读总结】inception v4与Inception-ResNet总结

Inception-v4, Inception-ResNet和Residual connections对学习的影响1.摘要2.引言3.文献综述4.体系结构的选择4.1 Pure Inception4.2 Residual Inception Blocks【残差Inception 块】4.3 Scaling of the Residuals【残差的缩放】4.3.1实验发现4.3.1.1实验发现14.3.1.2实验发现2…

ERP容灾备份维护工作有哪些?服务器容灾备份

ERP维护工作有哪些&#xff1f;这是公司信息化专员工作职责&#xff1a;信息规划  1、参与 公司信息化系统总体构架&#xff0c;建立健全公司信息化各项管理制度和标准业务流程&#xff0c;组织公司各业务部门不断进行业务流程的梳理、优化和创新&#xff0c;推动信息化的持续…

408 考研《操作系统》第三章第二节:内存管理、覆盖和交换 、连续分配管理方式、动态分区分配算法

文章目录1. 内存管理的概念1.1 内存保护1.2 总结2. 覆盖与交换2.1 覆盖技术2.2 交换技术2.3 总结&#xff1a;3. 连续分配管理方式3.1 单一连续分配3.2 固定分区分配3.3 动态分区分配3.4 总结4. 动态分区分配算法4.1 首次适应算法4.2 最佳适应算法4.3 最坏适应算法4.4 邻近适应…

米尔基于ARM架构核心板的国产化EtherCAT主站控制器解决方案

EtherCAT是由德国BECKHOFF自动化公司于2003年提出的实时工业以太网技术。它具有高速和高数据有效率的特点&#xff0c;支持多种设备连接拓扑结构。其从站节点使用专用的控制芯片&#xff0c;主站使用标准的以太网控制器。 EtherCAT是一种工业以太网技术&#xff0c;看到的大多…

【RocketMQ】RocketMQ实例--顺序消息

1、应用场景 一、以证券股票交易撮合场景为例&#xff0c;对于出价相同的交易单&#xff0c;坚持按照先出价先交易的原则&#xff0c;下游处理订单的系统需要严格按照出价顺序来处理订单。 二、以数据库变更增量同步场景为例&#xff0c;上游源端数据库按需执行增删改操作&…