我有一个带有子节点(节点)的自定义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中,以便我可以从任何我想要的地方加载数据。我的意思是从这个等级的结尾,中间或开始。
我认为该行的关系应该是邻接关系和父子关系。但我不知道该怎么做。
答案 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;
treemodel表只包含结构标识符:我会将节点数据保存在一个单独的表中,但我不会加入来获取它,而是我会执行第二个select ... where node in (...)
- 这基本上是说'我的数据结构独立于我的数据'
此模型旨在限制到数据库的往返次数,可能看起来违反直觉,但允许您在没有连接的单个数据库指令中读取或锁定树的部分
这会与您的数据模型相反,因为您没有为嵌套子项存储额外的“主体”节点 - 但是如果您可以更改此结构,那么您可以利用此提议来避免查询在循环内,即。多个selects
,或可重入查询或自/一元联接
...但您的业务逻辑需要支持我称之为“主要”节点的概念
取决于你在主要节点放置什么的用例 - 我使用这个模型来存储观察记录和它们的派生之间的因果关系,而不管这一点下面的父子关系 - 另一个示例可能是:1)客户端引发支持案例,或2)发送新消息,或3)创建新文件,...
将主体存储为树结构中的实际根节点(即节点标识'1'或'数据目录'或其他)是没有意义的 - 而是存储'下一个为了论证而降级,即。 “用户根目录”或“用户根目录下的第一级” - 这是了解您的用例和范围的地方。业务规则将有所帮助
...并且您的Java代码需要更新以查找或复制并存储此概念 - 它始终是来自父节点的副本,位于给定分支内的任何insert
上树 - 如果您要移动分支,并且您的逻辑需要更改此数字,则为update ... set principal=y where principal=x
和update ... set parent=newid where node=current_principal
...并且已经说了所有这些,我不会更新行本身,只更改insert
更改并重新创建整个树< / strike> branch(解释state
字段,即CURRENT
,DELETED
,...其中已删除分支的根节点仍然指向其当前父节点,例如。对于“已删除的项目”)
你仍然保持指向prev&amp; amp;中每个相邻节点的指针next - 在最坏的情况下,将节点插入到树的分支中会需要select ... where principal=x for update
,但可能只需要select ... where (node=x or prev=x or next=x) for update
编辑:
主键/聚簇索引必须是唯一的 - 您还可以在principal
上进行分区以确保快速并行访问
重新创建而不是更新分支
答案 1 :(得分:0)
一年前我有同样的要求然后我探索了很多选择。然后我决定使用图形数据库。试用图形数据库Neo4j。