# 数据结构

# BST树(二叉搜索树)

# 特征

  1. 每个节点最多有两个子节点
  2. 每个节点都包含一个Compareable的键
  3. 每个节点的值都大于左子树任意节点的值
  4. 每个节点的值都小于右子树任意节点的值
  5. 所有节点的值都各不相等
  6. 二叉搜索树可以是一颗空树

# Java代码实现

/**
 * @author chenpeng
 * @date 2019/5/14
 */
public class BinarySearchTree<K extends Comparable<K>> {

    /**
     * 插入节点
     *
     * @param node 二叉树
     * @return 新的二叉树
     */
    public Node<K> put(Node<K> node, K key) {
        if (node == null) {
            return new Node<K>(key);
        }

        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            node.left = put(node.left, key);
        } else if (cmp > 0) {
            node.right = put(node.right, key);
        } else {
            throw new IllegalArgumentException("repeat data can not add in bst");
        }
        node.count = size(node.left) + size(node.right) + 1;
        return node;
    }

    /**
     * 二叉树搜索
     *
     * @param node 二叉树
     * @param key  要搜索的值
     * @return 匹配节点,null表示未查询到
     */
    public Node<K> get(Node<K> node, K key) {
        if (node == null) {
            return null;
        }
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            return get(node.left, key);
        } else if (cmp > 0) {
            return get(node.right, key);
        }
        return node;
    }

    /**
     * 获取指定节点的子节点总数
     *
     * @param node 给定节点
     * @return 节点总数
     */
    public int size(Node<K> node) {
        if (node == null) {
            return 0;
        }
        return node.count;
    }

    /**
     * 找到最小节点
     *
     * @param node 给定二叉树
     * @return 最小节点
     */
    public Node<K> min(Node<K> node) {
        if (node == null) {
            return null;
        }
        if (node.left == null) {
            return node;
        }
        return min(node.left);
    }

    /**
     * 删除指定树的最小节点
     *
     * @param node 给定节点
     * @return 删除节点后的二叉树
     */
    public Node<K> deleteMin(Node<K> node) {
        if (node == null) {
            return null;
        }
        if (node.left == null) {
            return node.right;
        }
        node.left = deleteMin(node.left);
        node.count = size(node.left) + size(node.right) + 1;
        return node;
    }

    /**
     * 删除操作(假设n是要被删除的节点)
     * 1. n.left==null,删除了 n 节点,n.right就是根节点
     * 2. n.rigt==null, 删除了 n 节点, n.left就是根节点
     * 3. n.left!=null && n.right!=null ,那么就取 n.right 的最小节点替代被删除的节点,整个树的有序性依然能够保持
     *
     * @param node 给定节点
     * @param key  要删除的key
     * @return 删除后的节点
     */
    public Node<K> delete(Node<K> node, K key) {
        if (node == null) {
            return null;
        }
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            node.left = delete(node.left, key);
        } else if (cmp > 0) {
            node.right = delete(node.right, key);
        } else {
            if (node.left == null && node.right == null) {
                return null;
            }

            //左节点为空,直接取右节点作为新节点
            if (node.left == null) {
                return node.right;
            }

            //右节点为空,直接取左节点为新节点
            if (node.right == null) {
                return node.left;
            }

            Node<K> t = node;
            node = min(node.right);
            node.right = deleteMin(t.right);
            node.left = t.left;
        }

        node.count = size(node.left) + size(node.right) + 1;
        return node;
    }
}

class Node<K extends Comparable<?>> {
    K key;
    Node<K> left;
    Node<K> right;
    /**
     * 节点总量
     */
    int count;

    public Node(K key) {
        this.key = key;
        this.count = 1;
    }
}

# AVL树(平衡二叉树)

AVL树,也称平衡二叉树,AVL是其发明者Adelson-Velsky and Landis的简写,AVL树属于树的一种,而它也是一个二叉搜索树,不同的是他通过一定机制能够保证二叉树的平衡,平衡的二叉搜索树的查询效率更高。

# 特征

  1. AVL树是一种二叉搜索树,并且会在插入删除的时候通过旋转达到自平衡
  2. AVL树拥有二叉搜索树的所有基本特点
  3. AVL树的每个节点的左右子树的最大高度都不会超过1
  4. 普通的二叉搜索树查询效率是O(h),h表示二叉树的高度,极端情况下,可能会达到O(n),AVL树的自平衡可以保证其查询效率始终能达到O(Logn)

# AVL树的优势

​ 普通的二叉搜索树的查询效率是O(h),其中h表示二叉树的高度。但是在极端情况下,所有的节点都在树的一边,二叉树退化成了链表,此时二叉树的查询效率就变成了O(n)。AVL树则通过旋转,使得树的高度始终能保持在logn,在牺牲插入性能的条件下达到了提高查询效率的目的。

# 平衡因子

节点N的平衡因子可以通过下面的公式计算出来:BalanceFactory(N) = Height(N.right)-Height(N.left)(也就是左子树的高度-右子树的高度。)

avl树

绿色数字表示该节点的平衡因子,只有所有节点的平衡因子在 {-1,0,1} 内, 这棵树才是平衡的。

# 判断树是否平衡

/**
 * 节点是否平衡
 *
 * @param node 给定节点
 * @return true-平衡 false-不平衡
 */
public boolean isBalanced(Node<K> node) {
    if (node == null) {
        return true;
    }
    return Math.abs(height(node.left) - height(node.right)) <= 1 && isBalanced(node.left) && isBalanced(node.right);
}

/**
 * 计算节点高度
 *
 * @param node 给定节点
 * @return 节点高度
 */
public int height(Node<K> node) {
    if (node == null) {
        return 0;
    }
    return Math.max(height(node.left), height(node.right)) + 1;
}

# AVL树的旋转

  1. 当插入和删除数据的时候,有可能会破坏树的平衡,这时AVL树会通过旋转的方式来维持整颗树的平衡。
  2. 旋转只有两种: 左旋和右旋,像左右旋转和右左旋转都是在这个基础上做出来的。
  3. 每次旋转都要重新计算节点高度,这是判断后续节点是否平衡的重要依据。

# 左旋

avl树左旋

/**
 * 左旋转
 *
 * @param x 根节点
 * @return 旋转后的根节点
 */
public Node<K> leftRotation(Node<K> x) {
    Node<K> y = x.right;
    x.right = y.left;
    y.left = x;

    x.height = Math.max(height(x.left), height(x.right)) + 1;
    y.height = Math.max(height(y.left), height(y.right)) + 1;
    return y;
}

# 右旋

avl树右旋

/**
 * 右旋转
 *
 * @param y 根节点
 * @return 旋转后的根节点
 */
public Node<K> rightRotation(Node<K> y) {
    Node<K> x = y.left;
    y.left = x.right;
    x.right = y;

    y.height = Math.max(height(y.left), height(y.right)) + 1;
    x.height = Math.max(height(x.left), height(x.right)) + 1;
    return x;
}

# Java代码实现

public class SelfBalancingBinarySearchTree<K extends Comparable<K>> {

    /**
     * 左旋转
     *
     * @param x 根节点
     * @return 旋转后的根节点
     */
    public Node<K> leftRotation(Node<K> x) {
        Node<K> y = x.right;
        x.right = y.left;
        y.left = x;

        x.height = Math.max(height(x.left), height(x.right)) + 1;
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        return y;
    }

    /**
     * 右旋转
     *
     * @param y 根节点
     * @return 旋转后的根节点
     */
    public Node<K> rightRotation(Node<K> y) {
        Node<K> x = y.left;
        y.left = x.right;
        x.right = y;

        y.height = Math.max(height(y.left), height(y.right)) + 1;
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        return x;
    }

    public int getBalance(Node<K> node) {
        if (node == null) {
            return 0;
        }

        return height(node.left) - height(node.right);
    }

    /**
     * 插入节点
     *
     * @param node 二叉树
     * @return 新的二叉树
     */
    public Node<K> put(Node<K> node, K key) {
        if (node == null) {
            return new Node<>(key);
        }

        //1.插入数据
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            node.left = put(node.left, key);
        } else if (cmp > 0) {
            node.right = put(node.right, key);
        } else {
            throw new IllegalArgumentException("duplicate keys not allowed");
        }

        //2.更新上级节点高度
        node.height = 1 + Math.max(height(node.left), height(node.right));

        //3.计算平衡因子,判断平衡是否被破坏
        int balance = getBalance(node);
        if (balance <= 1 && balance >= -1) {
            return node;
        }
        //4.平衡被破坏,通过旋转来调整平衡
        return reBalance(node);
    }

    /**
     * 二叉树搜索
     *
     * @param node 二叉树
     * @param key  要搜索的值
     * @return 匹配节点,null表示未查询到
     */
    public Node<K> get(Node<K> node, K key) {
        if (node == null) {
            return null;
        }
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            return get(node.left, key);
        } else if (cmp > 0) {
            return get(node.right, key);
        }
        return node;
    }

    /**
     * 找到最小节点
     *
     * @param node 给定二叉树
     * @return 最小节点
     */
    public Node<K> min(Node<K> node) {
        if (node == null) {
            return null;
        }
        if (node.left == null) {
            return node;
        }
        return min(node.left);
    }

    /**
     * 删除操作(假设n是要被删除的节点)
     * 1. n.left==null,删除了 n 节点,n.right就是根节点
     * 2. n.rigt==null, 删除了 n 节点, n.left就是根节点
     * 3. n.left!=null && n.right!=null ,那么就取 n.right 的最小节点替代被删除的节点,整个树的有序性依然能够保持
     * 4. 节点删除后, 判断树有没有失去平衡,如果失去平衡根据删除了左节点还是右节点来判断如何旋转
     *
     * @param root 给定节点
     * @param key  要删除的key
     * @return 删除后的节点
     */
    public Node<K> delete(Node<K> root, K key) throws CloneNotSupportedException {
        if (root == null) {
            return null;
        }
        int cmp = key.compareTo(root.key);
        if (cmp < 0) {
            root.left = delete(root.left, key);
        } else if (cmp > 0) {
            root.right = delete(root.right, key);
        } else {
            if (root.left == null && root.right == null) {
                return null;
            } else if (root.left == null) {
                //左节点为空,直接取右节点作为新节点
                root = root.right;
            } else if (root.right == null) {
                //右节点为空,直接取左节点为新节点
                root = root.left;
            } else {
                //左右节点都不为空
                //找到右子树上最小的节点
                Node<K> temp = min(root.right);
                //将最小节点的值拷贝过来
                root.key = temp.key;
                //删除最小值
                root.right = delete(root.right, temp.key);
            }
        }
        //重新计算高度
        root.height = Math.max(height(root.left), height(root.right)) + 1;
        return reBalance(root);
    }

    /**
     * 重新平衡二叉树
     *
     * @param root 根节点
     * @return
     */
    public Node<K> reBalance(Node<K> root) {
        int balance = getBalance(root);
        /**
         * 左旋转
         *        x                     y
         *       / \                   / \
         *      y  T1                T3   x
         *     / \         ==>      /    / \
         *    T3  T2               T4   T2 T1
         *   /
         *  T4
         */
        if (balance > 1 && getBalance(root.left) >= 0) {
            return rightRotation(root);
        }

        /**
         * 左右旋转
         *      x                     x              T2
         *     / \                   / \            / \
         *    y  T1                T2  T1          y   x
         *   / \          ==>     /         ==>   / \   \
         *  T3  T2               y              T3  T4  T1
         *     /                / \
         *    T4               T3 T4
         */
        if (balance > 1 && getBalance(root.left) < 0) {
            //对y节点进行左旋
            root.left = leftRotation(root.left);
            //对x节点进行右旋
            return rightRotation(root);
        }

        /**
         * 右旋转
         *        x                     y
         *       / \                   / \
         *      T1  y                 x   T3
         *         / \       ==>     / \   \
         *        T2 T3             T1 T2  T4
         *            \
         *            T4
         */
        if (balance < -1 && getBalance(root.right) <= 0) {
            return leftRotation(root);
        }

        /**
         * 右左旋转
         *        x                     x                     T2
         *       / \                   / \                   / \
         *      T1  y                 T1 T2                 x   y
         *         / \       ==>          \      ==>       /   / \
         *        T2 T3                    y              T1  T4 T3
         *         \                      / \
         *         T4                    T4 T3
         */
        if (balance < -1 && getBalance(root.right) > 0) {
            //对y节点进行右旋
            root.right = rightRotation(root.right);
            //对x节点进行左旋
            return leftRotation(root);
        }
        return root;
    }


    /**
     * 获取节点高度
     *
     * @param node 给定节点
     * @return 节点高度
     */
    public int height(Node<K> node) {
        if (node == null) {
            return 0;
        }
        return node.height;
    }

    /**
     * 节点是否平衡
     *
     * @param node 给定节点
     * @return true-平衡 false-不平衡
     */
    public boolean isBalanced(Node<K> node) {
        if (node == null) {
            return true;
        }
        return Math.abs(calculateHeight(node.left) - calculateHeight(node.right)) <= 1 && isBalanced(node.left) && isBalanced(node.right);
    }

    /**
     * 计算节点高度
     *
     * @param node 给定节点
     * @return 节点高度
     */
    public int calculateHeight(Node<K> node) {
        if (node == null) {
            return 0;
        }
        return Math.max(calculateHeight(node.left), calculateHeight(node.right)) + 1;
    }
}
public class Node<K extends Comparable<?>> implements Cloneable {
    K key;
    Node<K> left;
    Node<K> right;
    /**
     * 节点总量
     */
    int count;

    /**
     * 数的高度
     */
    int height;

    public Node(K key) {
        this.key = key;
        this.count = 1;
        this.height = 1;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

# 验证程序

public static void main(String... args) throws CloneNotSupportedException {
    List<Character> list = new ArrayList<>();
    for (int i = 'A'; i <= 'Z'; i++) {
        list.add((char) i);
    }
    for (int i = 0; i < 100000; i++) {
        //随机打乱顺序
        Collections.shuffle(list);
        //随机选择插入数量
        List<Character> subList = list.subList(0, new Random().nextInt(26) + 1);
        //插入删除校验
        balanceCheck(subList, null);
        System.out.println("balanced: " + subList);
    }
    System.out.println("all balanced.");
}

public static <K extends Comparable<K>> void balanceCheck(List<K> inputList, List<K> deleteList) throws CloneNotSupportedException {
    SelfBalancingBinarySearchTree<K> selfBalancingBinarySearchTree = new SelfBalancingBinarySearchTree<K>();
    Node<K> node = null;
    for (K key : inputList) {
        node = selfBalancingBinarySearchTree.put(node, key);
    }
    if (!selfBalancingBinarySearchTree.isBalanced(node)) {
        System.out.println("输入数据:" + inputList);
        BstPrintUtil.printNode(node);
        throw new RuntimeException("不平衡");
    }

    //随机删除多个数据
    int total = new Random().nextInt(inputList.size());
    if (deleteList == null || deleteList.isEmpty()) {
        deleteList = new ArrayList<K>();
        for (int i = 0; i < total; i++) {
            K toDelete = inputList.get(new Random().nextInt(inputList.size()));
            deleteList.add(toDelete);
        }
    }


    Node before;
    for (K k : deleteList) {
        before = (Node) node.clone();
        node = selfBalancingBinarySearchTree.delete(node, k);
        //判断删除后是否依然平衡
        if (!selfBalancingBinarySearchTree.isBalanced(node)) {
            System.out.println("before:");
            BstPrintUtil.printNode(before);
            System.out.println("inputList:" + inputList);
            System.out.println("deleteList:" + deleteList);
            System.out.println("删除数据 :" + k);
            System.out.println("after:");
            BstPrintUtil.printNode(node);
            throw new RuntimeException("删除后树不平衡");
        }
    }
}
public class BstPrintUtil {

    /**
     * 二维打印树
     *
     * @param root 给定二叉树
     */
    public static void printNode(Node<?> root) {
        int maxLevel = BstPrintUtil.maxLevel(root);
        List<Node<?>> nodes = new ArrayList<Node<?>>();
        nodes.add(root);
        printNodeInternal(nodes, 1, maxLevel);
    }

    private static void printNodeInternal(List<Node<?>> nodes, int level, int maxLevel) {
        if (nodes.isEmpty() || BstPrintUtil.isAllElementsNull(nodes)) {
            return;
        }

        int floor = maxLevel - level;
        int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
        int firstSpaces = (int) Math.pow(2, (floor)) - 1;
        int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;

        BstPrintUtil.printWhitespaces(firstSpaces);

        List<Node<?>> newNodes = new ArrayList<Node<?>>();
        for (Node<?> node : nodes) {
            if (node != null) {
                System.out.print(node.key);
                newNodes.add(node.left);
                newNodes.add(node.right);
            } else {
                newNodes.add(null);
                newNodes.add(null);
                System.out.print(" ");
            }

            BstPrintUtil.printWhitespaces(betweenSpaces);
        }
        System.out.println("");

        for (int i = 1; i <= endgeLines; i++) {
            for (Node<?> node : nodes) {
                BstPrintUtil.printWhitespaces(firstSpaces - i);
                if (node == null) {
                    BstPrintUtil.printWhitespaces(endgeLines + endgeLines + i + 1);
                    continue;
                }

                if (node.left != null) {
                    System.out.print("/");
                } else {
                    BstPrintUtil.printWhitespaces(1);
                }

                BstPrintUtil.printWhitespaces(i + i - 1);

                if (node.right != null) {
                    System.out.print("\\");
                } else {
                    BstPrintUtil.printWhitespaces(1);
                }

                BstPrintUtil.printWhitespaces(endgeLines + endgeLines - i);
            }

            System.out.println("");
        }

        printNodeInternal(newNodes, level + 1, maxLevel);
    }

    private static void printWhitespaces(int count) {
        for (int i = 0; i < count; i++) {
            System.out.print(" ");
        }
    }

    private static int maxLevel(Node<?> node) {
        if (node == null) {
            return 0;
        }

        return Math.max(BstPrintUtil.maxLevel(node.left), BstPrintUtil.maxLevel(node.right)) + 1;
    }

    private static <T> boolean isAllElementsNull(List<T> list) {
        for (Object object : list) {
            if (object != null) {
                return false;
            }
        }

        return true;
    }
}

class Node<K extends Comparable<?>> {
    K key;
    Node<K> left;
    Node<K> right;
    /**
     * 节点总量
     */
    int count;

    public Node(K key) {
        this.key = key;
        this.count = 1;
    }
    
    public static void main(String... args) {
        Node<Integer> node = new Node<Integer>(5);
        node.left = new Node<Integer>(1);
        node.left.right = new Node<Integer>(3);

        node.right = new Node<Integer>(7);
        node.right.left = new Node<Integer>(6);
        node.right.right = new Node<Integer>(8);

        BstPrintUtil.printNode(node);
    }
}

# 红黑树(特殊的平衡二叉树)

是一种自平衡二叉查找树,1972年由Rudolf Bayer发明,它与AVL树类似,都在插入和删除操作时能通过旋转保持二叉查找树的平衡,以便能获得高效的查找性能。它可以在O(logn)的时间内做查找,插入和删除等操作。红黑树是2-3-4树的一种等同,但有些红黑树设定只能左边是红树,这种情况就是2-3树的一种等同了。对于AVL树来说,红黑树牺牲了部分平衡性以换取插入\删除操作时少量的旋转操作,整体上来说性能要由于AVL。

参考网址:

https://www.jianshu.com/p/e136ec79235c (opens new window)

https://baijiahao.baidu.com/s?id=1641940303518144126&wfr=spider&for=pc (opens new window)

# 特征

  1. 节点分为红色或者黑色
  2. 根节点必为黑色
  3. 叶子节点都为黑色,且为null。 [注意:这里叶子结点,是指为空(NIL或NULL)的叶子结点!]
  4. 连接红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)
  5. 从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点。[确保没有一条路径会比其他路径长出俩倍,所以红黑树是相对接近平衡的二叉树的!]
  6. 新加入到红黑树的节点为红色节点

# B-树(多路搜索树,不一定是二叉的)

1970年,R.Bayer和E.mccreight提出了一种适用于外查找的树,它是一种平衡的多叉树,称为B树(或B-树)

参考:

https://blog.csdn.net/wyqwilliam/article/details/82935922 (opens new window)

https://www.e-learn.cn/content/qita/2465295 (opens new window)

# 特征:

# B+树

B+树是应文件系统所需而出的一种B-树的变型树

# 特征

# B*树

是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针

# 特征

# LSM树(日志结构合并树)

参考:

https://my.oschina.net/u/3768341/blog/1861086 (opens new window)

https://blog.csdn.net/las723/article/details/93767240 (opens new window)

# 对比三种引擎的实现

  1. Hash存储引擎:哈希表持久化的实现,可以快速支持增删改查等随机操作,且时间复杂度为o(1),但是不支持顺序读取扫描,对应的存储系统为k-v存储系统的实现。
  2. B+树存储引擎是b树的持久化实现,不仅支持单条记录的增删改查操作,还支持顺序扫描,对应的存储系统就是mysql。
  3. LSM树存储引擎和b树存储引擎,一样支持,增删改查,也支持顺序扫描操作。LSM牺牲了读性能,提高写性能。

# 核心思路

​ 就是假定内存足够大,因此不需要每次有数据更新就必须将数据写入到磁盘中,而可以先将最新的数据驻留在内存中,等到积累到最后多之后,再使用归并排序的方式将内存内的数据合并追加到磁盘队尾(因为所有待排序的树都是有序的,可以通过合并排序的方式快速合并到一起)。

​ 将对数据的修改增量保存在内存中,达到指定大小限制之后批量把数据flush到磁盘中,磁盘中树定期可以做merge操作,合并成一棵大树,以优化读性能。不过读取的时候稍微麻烦一些,读取时看这些数据在内存中,如果未能命中内存,则需要访问较多的磁盘文件。极端的说,基于LSM树实现的HBase写性能比MySQL高了一个数量级,读性能却低了一个数量级。

# LSM树

​ 理论上,可以是内存中树的一部分和磁盘中第一层树做merge,对于磁盘中的树直接做update操作有可能会破坏物理block的连续性,但是实际应用中,一般lsm有多层,当磁盘中的小树合并成一个大树的时候,可以重新排好顺序,使得block连续,优化读性能。

​ 一般数据库的存储一定要保持有序,有序是一个非常重要的概念(当然hash结构的除外,hash不支持顺序扫描,对应的存储系统为key-value存储系统。对于key-value的插入以及查询,哈希表的复杂度都是O(1),明显比树的操作O(n)快,如果不需要有序的遍历数据,哈希表就是O(1) ).

​ LSM树相比于B+树(大量的叶节点操作,不仅支持单条记录的增、删、读、改操作,还支持顺序扫描(B+树的叶子节点之间的指针), 对B树的写入过程是一次原位写入的过程,主要分为两个部分,首先是查找到对应的块的位置,然后将新数据写入到刚才查找到的数据块中,然后再查找到块所对应的磁盘物理位置,将数据写入去。当然,在内存比较充足的时候,因为B树的一部分可以被缓存在内存中,所以查找块的过程有一定概率可以在内存内完成,不过为了表述清晰,我们就假定内存很小,只够存一个B树块大小的数据吧。可以看到,在上面的模式中,需要两次随机寻道(一次查找,一次原位写),才能够完成一次数据的写入,代价还是很高的。 ), 弄了很多个小的有序结构,比如每m个数据,在内存里排序一次,下面100个数据,再排序一次……这样依次做下去,我就可以获得N/m个有序的小的有序结构。 在查询的时候,因为不知道这个数据到底是在哪里,所以就从最新的一个小的有序结构里做二分查找,找得到就返回,找不到就继续找下一个小有序结构,一直到找到为止。 很容易可以看出,这样的模式,读取的时间复杂度是(N/m)*log2N 。读取效率是会下降的。

​ LSM树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树会flush到磁盘中,磁盘中的树定期可以做merge操作,合并成一棵大树,以优化读性能。

​ 总结为,LSM树并不是像B+树单次在磁盘寻址,根据索引来进行写入。而是大量堆集内存,达到一定阈值后,写入磁盘,因为是批量处理,磁盘IO对其性能影响很小,对写操作的性能大大提升。然后再磁盘内再进行合并(Merge)操作,再来提升读性能,可见,对于即时读取的性能不高,不可能写进去马上就读出来,而是要经过一个Merge的过程。

# 优化

  1. Bloom filter: 就是个带随即概率的bitmap,可以快速的告诉你,某一个小的有序结构里有没有指定的那个数据的。于是就可以不用二分查找,而只需简单的计算几次就能知道数据是否在某个小集合里啦。效率得到了提升,但付出的是空间代价。
  2. compact:小树合并为大树:因为小树他性能有问题,所以要有个进程不断地将小树合并到大树上,这样大部分的老数据查询也可以直接使用log2N的方式找到,不需要再进行(N/m)*log2n的查询了。

# 使用LSM树的案例

  1. rocksDB
  2. levelDB
  3. hbase
Last Updated: 6/15/2020, 1:01:06 AM