保持分层有序列表(flatfile / sql / nosql)

时间:2011-01-04 16:32:59

标签: data-structures persistence hierarchical-data html-lists

我想存储分层有序列表。一个例子是嵌套的待办事项列表。另一个例子是XML。它只是一棵树,孩子们在这里。为简单起见,条目只是文本字符串。

问题是用户会编辑列表,因此常见操作很快很重要:

  • 编辑元素
  • 删除元素
  • 在另一个
  • 之前插入一个条目

我可以想象如何在数据结构中执行此操作:条目是链接列表,如果它们包含子项,则它们也指向另一个链接列表的头部。有一个哈希表将条目id与实际数据相关联。

  • 编辑正在查找哈希值,然后替换链表的数据部分
  • 删除正在查找哈希并执行链表删除
  • 插入正在查找哈希并执行链表插入

但是,我需要存储数据,我不知道如何实现这一点。如果只有一个元素发生变化,我不想保存整个树。什么是最好的方法?平面文件/ SQLs / NoSqls / voodoos?

2 个答案:

答案 0 :(得分:1)

您存储列表(或更确切地说是树),并且一旦它的一小部分发生变化,就不希望重写整个树。由此我得出结论,结构是巨大的,并且相对经常发生微小的变化。

链接列表都是关于指针追逐的,指针和它们引用的内容很像键和值。您需要有效地存储键值对。项目顺序由链表结构保留。

假设您使用典型的键值存储,从xDBMBerkeley DB到任何现代NoSQL产品。你也可以采用一个紧凑的SQL引擎,例如sqlite。它们通常使用树来索引键,因此需要O(logN)来访问密钥,或者哈希表需要大约或更少。

您没有指定何时以增量方式保留数据。如果您只是偶尔执行一次(不是每次更新),您需要有效地将数据库与主数据结构进行比较。这将是相对耗时的,因为您需要遍历整个树并查看数据库中的每个节点ID。这是对数但由于必要的I / O而具有巨大的常数。然后你会想要从不再引用的项目中清除持久存储。可能会发生只是将树转储为JSON效率更高。事实上,这就是许多内存数据库所做的事情。

如果您在每次更新主结构时更新持久性结构,那么无论如何都没有必要拥有该主结构。最好用内存键值存储替换它,例如已经具有持久性机制的Redis(以及其他一些好东西)。

答案 1 :(得分:1)

使用关系数据库是可行的解决方案。根据您的需要 - 快速插入,更新,删除 - 我会使用附加列表以及其他自定义项:

id 
parent_id
cardinality -- sort order for all nodes with the same parent_id
depth -- distance from the root node

计算cardinalitydepth可以使用代码完成,也可以 - 最好是 - 用于任何插入,删除或更新的数据库触发器。此外,为了使用一个SELECT语句检索整个层次结构,将调用层次结构桥接表:

id
descendent_id 

此表也将通过上述相同的触发器填充,并用作检索给定id上方或下方的所有节点的方法。

See this question for additional detail around Adjacency List, Hierarchy Bridge and other approaches for storing hierarchical data in a relational database

最后,对您列出的选项提供一些补充说明:

  • 平面文件:链接列表和内存映射文件的组合可能会起作用,但是你真的只是在那时自己滚动,SQL或NoSQL解决方案可能会做得更好。
  • SQL :这将是我的方法 - 工具是数据处理,备份和恢复的最佳选择。
    • XML :这也是数据库的一种可能性,非常特定于供应商,您需要研究节点插入,更新和删除的语法。如果数据库提供XML数据类型,则可以非常快。
  • NoSQL :如果您正在谈论key-value storagetypical approach for hierarchical data appears to be materialized path,但这需要重新计算更改时所有受影响节点的整个路径,这可能很慢。相反,考虑Java Content Repository(JCR) - Apache Jackrabbit是一种实现 - 整个API围绕表示层次结构化数据并持久化 - 可能对您试图解决的问题过于沉重。
  • voodoo :嗯...

<强>更新

如果你实现了这个答案的所有部分,添加便宜,重新排序是小成本,移动是昂贵的。权衡是快速层次遍历读取 - 例如在一次操作中找到节点的完整祖先。具体而言,添加叶子是O(1)操作。重新排序意味着更新移动节点之后的所有对等节点的基数。移动意味着更新(1)源和目标对等节点的基数,(2)移动 - 和后代 - 节点深度,以及(3)移除和添加祖先到层次桥接表。

但是,单独使用Adjancency List(即id, parent_id)并且写入变得便宜,一个级别的读取便宜,但遍历层次结构的读取是昂贵的。后者需要使用递归SQL,如SQL Server和其他RDBMS中的Oracle的CONNECT BY或Common Table Expressions。