【问题标题】:Creating a disk-based data structure创建基于磁盘的数据结构
【发布时间】:2015-10-13 08:27:26
【问题描述】:

我找不到有关此主题的任何资源。有几个问题的答案很好,描述了需要存储在磁盘上的数据的问题的解决方案(pickle、shelve、一般的数据库),但我想学习如何实现我自己的。

1) 如果我要在 Python 中创建基于磁盘的图形结构,我必须通过写入磁盘来实现必要的方法。但是我该怎么做呢?

2) 基于磁盘的结构的好处之一是在处理可能不完全适合内存的数据时具有结构的效率。如果数据不适合内存,则一次只能访问其中的某些部分。如何一次只访问结构的一部分?

【问题讨论】:

标签: python file data-structures disk


【解决方案1】:

你需要解决很多问题,有些很简单,有些更复杂,但既然你想自己做,我认为你不介意自己填写细节(所以我会跳过一些部分)。

第一个简单的步骤是序列化和反序列化节点(以便能够完全存储在磁盘上)。这可以通过让您的节点具有 serialize/deserialize 方法以特别的方式完成 - 此外,您可能希望序列化数据具有类型指示符,以便您可以知道哪个类'deserialize应该用来反序列化数据。请注意,一个节点在磁盘上的表示必须通过文件偏移量(直接或间接)引用其他节点。

数据的实际读写是通过普通(二进制)文件操作来完成的,但是你必须先在文件中寻找到正确的位置。

第二步是有可能在文件中分配空间。如果您只想拥有一次写入的行为,那么只需增大文件就可以了,但是如果您想修改文件中的数据(添加和删除节点甚至替换它们),您将不得不应对以下情况文件中不再使用的区域,或者重复使用这些区域,甚至打包文件的布局。

进一步的步骤可能涉及在某种意义上使更新原子化。一种解决方案是有一个区域,您可以在其中写入足够的信息,以便更新可以完成(或放弃),如果它以最简单的形式过早终止,它可能只是一个幂等操作的列表(如果操作会产生相同的结果,如果您重复它们,将特定数据写入文件中的特定位置)。

请注意,虽然(某些)内置解决方案确实可以处理将整个图形写入磁盘或从磁盘读取整个图形,但它们并不能真正处理您只想读取图形的一部分或非常有效地修改图形的情况(您必须主要阅读整个图表并一口气写出完整的图表)。数据库是一个例外,您可以以随机方式读取/写入数据的较小部分。

【讨论】:

    【解决方案2】:

    您可以从simple 开始。假设您将图表存储在字典中:

    #!/usr/bin/env python2
    # coding: utf-8
    
    def read(filename):
        """
        Read a graph from a file. Returns a dictionary.
        The file must be in the format:
    
        SRC DST DST ...
        SRC DST
        ...
    
        where SRC and DST are names of nodes.
        Node names must not contain whitespace.
        """
        g = {}
        with open(filename) as handle:
            for line in handle:
                line = line.strip()
                if line == "":
                    continue
                parts = line.split()
                src, targets = parts[0], parts[1:]
                if src not in g:
                    g[src] = set()
                for target in targets:
                    g[src].add(target)
        return g
    
    def write(filename, g):
        """ Write dictionary `g` to file. """
        with open(filename, "w") as handle:
            for src, targets in g.iteritems():
                handle.write("%s %s\n" % (src, " ".join(targets)))
    

    示例用法:

    if __name__ == '__main__':
        g = {
            "A": ["B", "C"],
            "B": ["D"],
            "C": ["B"],
        }
        write("test.g", g)
        g = read("test.g")
        print(g) # {'A': set(['C', 'B']), 'C': set(['B']), 'B': set(['D'])}
    

    上面定义了一个图的简单序列化格式,实现了读写方法。虽然效率确实很低,但您可以单独使用这两种方法创建、更新和更改图表并将它们保存到磁盘。

    这会将整个图形存储在内存中。作为下一个优化,您可以编写方法 - 例如read_node(filename, nodename) 只会将给定节点加载到内存中。通过这种方式,您可以存储和使用大于可用内存的图形。

    这当然仍然非常低效,因为您需要读取整个文件才能找到您正在寻找的节点。

    然后您可以添加进一步的优化,例如存储排序的数据并使用二进制搜索快速找到具有给定名称的节点。或者,您可以沿图表数据存储其他数据以用于索引目的。您可以将一个小索引加载到内存中,查找您关心的节点,然后寻找存储数据的位置并仅读取相关块。

    等等。经过大量探索后,您可能会得到更高级的数据结构,例如 B-treesLog-structured merge-treesinverted indices

    【讨论】:

      猜你喜欢
      • 2011-05-11
      • 2012-09-19
      • 2011-04-12
      • 2011-07-02
      • 1970-01-01
      • 1970-01-01
      • 2017-03-30
      • 1970-01-01
      • 2011-04-20
      相关资源
      最近更新 更多