如何在关系数据库中存储带有孩子的链接树?
Posted
技术标签:
【中文标题】如何在关系数据库中存储带有孩子的链接树?【英文标题】:How to store Linked Tree with children in Relational Database? 【发布时间】:2018-11-07 03:38:32 【问题描述】:我有一个带有子(节点)的自定义 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 中,这样我就可以从任何我想要的地方加载数据。我的意思是从这个层次结构的末端、中间或开始。
我想该行的关系应该同时处于邻接关系和父子关系。但我不知道该怎么做。
【问题讨论】:
您是否考虑过像OrientDB 或Neo4J 这样的图形数据库?像这样的东西可能更适合您的要求。 大约一年前我都使用过它们。您是最适合解决此问题的图形数据库。但是还有一个问题,我已经在 MySQL 中存储了一些原始数据,在两种数据库中存储数据对我来说有点奇怪 没有简单的答案 - 选择取决于预期的使用模式。这个关于分层数据的 SQL 模型的精彩演示 slideshare.net/billkarwin/models-for-hierarchical-data 可能会有所帮助。或谷歌Joe Celko "SQL FOR SMARTIES"
。
【参考方案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) 创建新文件,...
将主体存储为树结构中的实际根节点是没有意义的(即节点 id '1' 或 'data directory' 或其他) - 相反,您将存储 'next为了争论而降低水平,即。 “用户根目录”或“用户根目录下的第一级” - 了解您的用例和业务规则会有所帮助
... 并且您的 java 代码将需要更新以查找或复制和存储此概念 - 它始终是树的给定分支内任何 insert
上的父节点的副本 -如果您要移动分支并且您的逻辑需要更改此数字,则它是 update ... set principal=y where principal=x
和 update ... set parent=newid where node=current_principal
... 说了这么多,我不会更新行本身,只更新insert
并重新创建整个树 分支(解释了state
字段,即CURRENT
,DELETED
,...其中已删除分支的根节点仍指向其当前父节点,例如“已删除项目”)
你仍然在 prev 和 next 中保留指向每个相邻节点的指针 - 在最坏的情况下,将节点有序插入到树的分支中,需要 select ... where principal=x for update
,但可能只需要 select ... where (node=x or prev=x or next=x) for update
编辑:
主键/聚集索引必须是唯一的 - 您也可以在 principal
上进行分区以确保快速并行访问
重新创建而不是更新分支
【讨论】:
@ste7 这里有更多解决方案,其中一个是由在其他 cmets 中链接的幻灯片共享中提出闭包模型的同一个人创作的:(***.com/q/192220/4036945) - 出于某种原因我无法评论您的问题,只能回答我的问题!? 谢谢。我认为您可以在答案末尾添加此链接作为附加信息。【参考方案2】:一年前我有同样的要求,然后我探索了很多选择。然后我决定使用图形数据库。试用 Neo4j,一个图形数据库。
【讨论】:
我之前用过Neo4J,写几百万行速度很慢。我更喜欢 OrientDB 作为 Grapch 数据库。以上是关于如何在关系数据库中存储带有孩子的链接树?的主要内容,如果未能解决你的问题,请参考以下文章