我正在为一组文档上的搜索引擎编写倒排索引。现在,我将索引存储为字典词典。也就是说,每个关键字都映射到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使用如此多的内存进行序列化?有没有更好的方法来解决这个问题?
答案 0 :(得分:10)
cPickle需要使用一堆额外的内存,因为它会进行循环检测。如果您确定数据没有循环,可以尝试使用编组模块
答案 1 :(得分:0)
你可以试试另一个泡菜库。您也可以更改一些cPickle设置。
其他选项:将您的字典分成更小的部分,然后cPickle每一部分。然后在装入所有东西时将它们重新组合在一起。
对不起,这很模糊,我只是写下了我的头脑。我认为它可能仍然有用,因为没有其他人回答。
答案 2 :(得分:0)
你可能正在使用错误的工具来完成这项工作。如果你想保留大量的索引数据,我强烈建议使用一个带有SQLObject或SQL Alchemy ORM的SQLite磁盘数据库(当然,只是一个普通的数据库)
这些将处理诸如兼容性,为目的优化格式等普通事物,并且不会同时将所有数据保存在内存中,以免耗尽内存......
已添加:因为无论如何我正在做一个几乎相同的事情,但主要是因为我是一个很好的人,这里有一个似乎可以做你需要的演示(它会创建一个当前dir中的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”表可能是一个单独的表,其中包含一些用于索引和唯一性的附加设置。