8

我正在为文档集合上的搜索引擎编写倒排索引。现在,我将索引存储为字典字典。也就是说,每个关键字都映射到 docIDs-> 出现位置的字典。

数据模型类似于: {word : { doc_name : [location_list] } }

在内存中构建索引工作正常,但是当我尝试序列化到磁盘时,我遇到了 MemoryError。这是我的代码:

# Write the index out to disk
serializedIndex = open(sys.argv[3], 'wb')
cPickle.dump(index, serializedIndex, cPickle.HIGHEST_PROTOCOL)

在序列化之前,我的程序使用了大约 50% 的内存(1.6 Gb)。当我调用 cPickle 时,我的内存使用量在崩溃前飙升至 80%。

为什么 cPickle 使用这么多内存进行序列化?有没有更好的方法来解决这个问题?

4

3 回答 3

10

cPickle 需要使用大量额外的内存,因为它会进行循环检测。如果您确定您的数据没有循环,您可以尝试使用 marshal 模块

于 2011-02-18T04:38:59.493 回答
0

您可以尝试其他泡菜库。此外,您可能还可以更改一些 cPickle 设置。

其他选项:将您的字典分成更小的部分,然后对每部分进行 cPickle。然后在加载所有内容时将它们重新组合在一起。

对不起,这是含糊的,我只是在脑海中写下。我认为它可能仍然有帮助,因为没有其他人回答。

于 2011-02-18T04:37:27.133 回答
0

您很可能在这项工作中使用了错误的工具。如果您想保留大量索引数据,我强烈建议您使用带有 ORM(如SQLObjectSQL Alchemy )的 SQLite 磁盘数据库(或者,当然,只是普通数据库) 。

这些将处理诸如兼容性、优化格式以及不同时将所有数据保存在内存中等平凡的事情,以免内存不足......

补充:因为无论如何我都在做几乎相同的事情,但主要是因为我是一个很好的人,这里有一个演示似乎可以满足您的需求(它将在您当前的目录中创建一个 SQLite 文件,然后将其删除如果同名的文件已经存在,那么先把它放在空的地方):

import sqlobject
from sqlobject import SQLObject, UnicodeCol, ForeignKey, IntCol, SQLMultipleJoin
import os

DB_NAME = "mydb"
ENCODING = "utf8"

class Document(SQLObject):
    dbName = UnicodeCol(dbEncoding=ENCODING)

class Location(SQLObject):
    """ Location of each individual occurrence of a word within a document.
    """
    dbWord = UnicodeCol(dbEncoding=ENCODING)
    dbDocument = ForeignKey('Document')
    dbLocation = IntCol()

TEST_DATA = {
    'one' : {
        'doc1' : [1,2,10],
        'doc3' : [6],
    },

    'two' : {
        'doc1' : [2, 13],
        'doc2' : [5,6,7],
    },

    'three' : {
        'doc3' : [1],
    },
}        

if __name__ == "__main__":
    db_filename = os.path.abspath(DB_NAME)
    if os.path.exists(db_filename):
        os.unlink(db_filename)
    connection = sqlobject.connectionForURI("sqlite:%s" % (db_filename))
    sqlobject.sqlhub.processConnection = connection

    # Create the tables
    Document.createTable()
    Location.createTable()

    # Import the dict data:
    for word, locs in TEST_DATA.items():
        for doc, indices in locs.items():
            sql_doc = Document(dbName=doc)
            for index in indices:
                Location(dbWord=word, dbDocument=sql_doc, dbLocation=index)

    # Let's check out the data... where can we find 'two'?
    locs_for_two = Location.selectBy(dbWord = 'two')

    # Or...
    # locs_for_two = Location.select(Location.q.dbWord == 'two')

    print "Word 'two' found at..."
    for loc in locs_for_two:
        print "Found: %s, p%s" % (loc.dbDocument.dbName, loc.dbLocation)

    # What documents have 'one' in them?
    docs_with_one = Location.selectBy(dbWord = 'one').throughTo.dbDocument

    print
    print "Word 'one' found in documents..."
    for doc in docs_with_one:
        print "Found: %s" % doc.dbName

这当然不是这样做的唯一方法(或不一定是最好的方法)。Document 或 Word 表是否应与 Location 表分开,取决于您的数据和典型用法。在您的情况下,“Word”表可能是一个单独的表,其中添加了一些索引和唯一性设置。

于 2011-02-18T05:54:17.870 回答