我在Python中实现了一个后缀树来进行全文搜索,并且它运行得非常好。但是有一个问题:索引文本可能非常大,因此我们无法将整个结构放在RAM中。
IMAGE:单词BANANAS
的后缀树(在我的场景中,想象一棵树大100000倍)。
所以,稍微研究一下,我找到了pickle
模块,一个很好的Python模块,用于从文件中“加载”和“转储”对象,猜猜是什么?它与我的数据结构完美配合。
因此,长话短说:在磁盘上存储和检索此结构的最佳策略是什么?我的意思是,一个解决方案可能是将每个节点存储在一个文件中,并在需要时从磁盘加载它,但这不是最好的想法(太多的磁盘访问)。
脚注:虽然我已将此问题标记为python,但编程语言并不是问题的重要部分,磁盘存储/检索策略确实是重点。
答案 0 :(得分:3)
管理这样的结构的有效方法是使用内存映射文件。在文件中,不是存储节点指针的引用,而是将偏移存储到文件中。您仍然可以使用pickle
将节点数据序列化为适合存储在磁盘上的流,但是您需要避免存储引用,因为pickle
模块将要同时嵌入整个树(正如你所见。)
使用mmap
模块,您可以将文件映射到地址空间,并像一个巨大的字节序列一样读取它。操作系统负责实际读取文件并管理文件缓冲区和所有细节。
您可以将第一个节点存储在文件的开头,并具有指向下一个节点的偏移量。读取下一个节点只需要读取文件中的正确偏移量。
内存映射文件的优点是它们不会一次性加载到内存中,但只在需要时从磁盘读取。我已经在安装了4 GB RAM的机器上使用30 GB文件(在64位操作系统上)完成了这项工作,并且工作正常。
答案 1 :(得分:3)
如果pickle
已经在为您工作,您可能需要查看ZODB,它会在pickle
之上添加一些功能。看一下这个文档,我看到了这个段落,它解决了你所关注的大小问题:
数据库在内存和存储之间自由移动对象。如果 对象在一段时间内没有被使用过,它可能会被释放出来 下次使用时从存储中加载的内容。
答案 2 :(得分:1)
如何将其存储在sqlite ?
SQLite的:
因此,仔细研究一下这个解决方案可能会很好,因为事实证明它是在应用程序中存储数据的有效方式。
答案 3 :(得分:1)
也许你可以将cPickle和bsddb
数据库结合起来,这样你就可以将你的pickled节点存储在一个类似于dictionnary的对象中,该对象将存储在文件系统中;因此,您可以稍后加载数据库,并从您需要的节点获得非常好的性能。
以一种非常简单的方式:
import bsddb
import cPickle
db = bsddb.btopen('/tmp/nodes.db', 'c')
def store_node(node, key, db):
db[key] = cPickle.dumps(node)
def get_node(key, db):
return cPickle.loads(db[key])
答案 4 :(得分:1)
尝试使用压缩后缀树。
主要思想是,不是拥有1个字符的大量节点,而是将它们压缩成多个字符的1个节点,从而节省额外的节点。
此链接(http://www.cs.sunysb.edu/~algorith/implement/suds/implement.shtml)表示您可以将160MB后缀树转换为33MB压缩后缀树。相当有益。
这些压缩树用于巨大字符串上的遗传子串匹配。我曾经用后缀树耗尽内存,但在压缩之后,内存不足错误就消失了。
我希望我能找到一篇无偿的文章来更好地解释实施。 (http://dl.acm.org/citation.cfm?id=1768593)