如何在关系数据库中存储子项的链接树?

时间:2018-05-28 14:01:26

标签: java mysql database tree data-persistence

我有一个带有子节点(节点)的自定义LinkedTree,节点具有邻接关系,即每个节点与上一个节点和下一个节点链接。这个LinkedTree非常重,很大,可能包含数百万个节点。

这是一个代码示例:

package tree;

import java.io.Serializable;

public class LinkedTree<E> implements Serializable {

    private int size = 0;
    private Node<E> first;
    private Node<E> last;
    private LinkedTree<E> children;

    public LinkedTree() {
        children = new LinkedTree<>();
    }

    public LinkedTree(LinkedTree<E> children) {
        this.children = children;
    }

    public void add(E element) {

        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, element, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
    }

    public void remove(E element) {

        ...
    }

    public int size() {

         return size;
    }

    public static class Node<E> implements Serializable {

        E item;
        Node<E> next;
        Node<E> prev;

        public Node(Node<E> prev, E item, Node<E> next) {

            this.item = item;
            this.next = next;
            this.prev = prev;
        }

        public E item() {

            return item;
        }

        public boolean hasPrevious() {

            return prev != null;
        }

        public Node<E> previous() {

            return prev;
        }

        public Node<E> previous(int target) {

            Node<E> n = this;
            int i = target;
            while (i-- > 0 && n != null)
                n = n.prev;
            return n;
        }

        public boolean hasNext() {

            return next != null;
        }

        public Node<E> next() {

            return next;
        }

        public E nextItem() {

            return next.item;
        }

        public E nextItem(int target) {

            return next(target).item();
        }

        public Node<E> next(int target) {

            Node<E> n = this;
            int i = 0;
            while (i++ < target && n != null)
                n = n.next;
            return n;
        }

        @Override
        public int hashCode() {

            return item != null ? item.hashCode() : 0;
        }

        @Override
        public boolean equals(Object o) {

            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            Node<?> node = (Node<?>) o;

            return item != null ? item.equals(node.item) : node.item == null;
        }

        @Override
        public String toString() {

            return item.toString();
        }
    }
}

我想序列化它并将其保存在文件中以使其持久化,但加载和写入一些数据可能成本太高。所以我决定把它保存在MySQL中,以便我可以从任何我想要的地方加载数据。我的意思是从这个等级的结尾,中间或开始。

我认为该行的关系应该是邻接关系和父子关系。但我不知道该怎么做。

2 个答案:

答案 0 :(得分:1)

我会评论要求提供更多信息(特别是样本数据和层次结构的规则),所以原谅第一次过于笼统的原因

我做了以下假设:

  • 您的结构深度超过三级 - 如果情况不是这样的话我不会这样做,因为它不值得吗

  • 您的负载读取很多,写入树的部分是并发的但没有冲突,而冲突的写入很少或根本不存在

  • 您不需要对树进行并行,部分和无共享或无锁访问来构建或处理它(在这种情况下,您需要发出deletes信号,您可以通过指向您替换的节点,即。被取代的)

我建议的数据模型看起来像:

create table treemodel (
 `node` int not null 
 , `parent` int not null 
 , `principal` int not null 
 , `state` smallint unsigned not null 
 , ...
 , `supersedes` int    /*version instead of lossy update or delete*/
 , `supersededby` int
) engine = innodb;
alter table treemodel add primary key (`principal`, `node`) using btree; 
  1. treemodel表只包含结构标识符:我会将节点数据保存在一个单独的表中,但我不会加入来获取它,而是我会执行第二个select ... where node in (...) - 这基本上是说'我的数据结构独立于我的数据'

  2. 此模型旨在限制到数据库的往返次数,可能看起来违反直觉,但允许您在没有连接的单个数据库指令中读取或锁定树的部分

  3. 这会与您的数据模型相反,因为您没有为嵌套子项存储额外的“主体”节点 - 但是如果您可以更改此结构,那么您可以利用此提议来避免查询在循环内,即。多个selects,或可重入查询或自/一元联接

  4. ...但您的业务逻辑需要支持我称之为“主要”节点的概念

  5. 取决于你在主要节点放置什么的用例 - 我使用这个模型来存储观察记录和它们的派生之间的因果关系,而不管这一点下面的父子关系 - 另一个示例可能是:1)客户端引发支持案例,或2)发送新消息,或3)创建新文件,...

  6. 将主体存储为树结构中的实际根节点(即节点标识'1'或'数据目录'或其他)是没有意义的 - 而是存储'下一个为了论证而降级,即。 “用户根目录”或“用户根目录下的第一级” - 这是了解您的用例和范围的地方。业务规则将有所帮助

  7. ...并且您的Java代码需要更新以查找或复制并存储此概念 - 它始终是来自父节点的副本,位于给定分支内的任何insert上树 - 如果您要移动分支,并且您的逻辑需要更改此数字,则为update ... set principal=y where principal=xupdate ... set parent=newid where node=current_principal

  8. ...并且已经说了所有这些,我不会更新行本身,只更改insert更改并重新创建整个树< / strike> branch(解释state字段,即CURRENTDELETED,...其中已删除分支的根节点仍然指向其当前父节点,例如。对于“已删除的项目”)

  9. 你仍然保持指向prev&amp; amp;中每个相邻节点的指针next - 在最坏的情况下,将节点插入到树的分支中会需要select ... where principal=x for update,但可能只需要select ... where (node=x or prev=x or next=x) for update

  10. 编辑:

    • 主键/聚簇索引必须是唯一的 - 您还可以在principal上进行分区以确保快速并行访问

    • 重新创建而不是更新分支

答案 1 :(得分:0)

一年前我有同样的要求然后我探索了很多选择。然后我决定使用图形数据库。试用图形数据库Neo4j。