Python 使用cPickle序列化大型字典会导致内存错误
我正在为一个搜索引擎在一组文档上写一个反向索引。现在,我将索引存储为一个字典字典。也就是说,每个关键字映射到docid->出现位置的字典 数据模型类似于: {单词:{文件名:[位置列表]} 在内存中构建索引工作正常,但当我尝试序列化到磁盘时,遇到了内存错误。这是我的密码:Python 使用cPickle序列化大型字典会导致内存错误,python,serialization,pickle,inverted-index,Python,Serialization,Pickle,Inverted Index,我正在为一个搜索引擎在一组文档上写一个反向索引。现在,我将索引存储为一个字典字典。也就是说,每个关键字映射到docid->出现位置的字典 数据模型类似于: {单词:{文件名:[位置列表]} 在内存中构建索引工作正常,但当我尝试序列化到磁盘时,遇到了内存错误。这是我的密码: # Write the index out to disk serializedIndex = open(sys.argv[3], 'wb') cPickle.dump(index, serializedIndex, cPic
# Write the index out to disk
serializedIndex = open(sys.argv[3], 'wb')
cPickle.dump(index, serializedIndex, cPickle.HIGHEST_PROTOCOL)
在序列化之前,我的程序使用了大约50%的内存(1.6GB)。我一打电话给cPickle,我的内存使用率就飙升到80%,然后崩溃
为什么cPickle使用这么多内存进行序列化?有没有更好的方法来解决这个问题?您可以尝试另一个pickle库。此外,您还可以更改某些cPickle设置 其他选择:把你的字典分成小块,然后把每一块都打包。然后当你把所有东西都装进去的时候,把它们放回一起
对不起,这是含糊不清的,我只是在胡思乱想。我想它可能仍然有用,因为没有其他人回答。cPickle需要使用大量额外的内存,因为它可以循环检测。如果您确定数据没有周期,可以尝试使用封送处理模块您很可能为此作业使用了错误的工具。如果您想要持久化大量索引数据,我强烈建议您将SQLite-on-disk数据库(或者,当然,只是一个普通的数据库)与ORM-like-or一起使用 这些将处理一些普通的事情,比如兼容性、为特定目的优化格式,以及不同时在内存中保存所有数据,从而耗尽内存 添加:因为我一直在做一件几乎相同的事情,但主要是因为我是一个很好的人,这里有一个演示,似乎可以满足您的需要(它将在当前目录中创建一个SQLite文件,如果已经存在同名文件,则将其删除,因此首先将其放在空的位置):
这当然不是做到这一点的唯一方法(或必然是最好的方法)。文档表或Word表是否应与位置表分开,取决于您的数据和典型用法。在您的例子中,“Word”表可能是一个单独的表,其中添加了一些索引和唯一性设置。工作起来很有魅力。难以置信的简单修复——基本上只是将“pickle”改为“marshal”就完成了。我没有意识到cPickle执行了周期检测。通过使用封送处理,写入磁盘只需几秒钟,而不是20分钟,并将内存消耗从30%减少到几乎0%。谢谢简单的解决方案加上简洁的解释,100%棒极了。@John我们怎么知道数据没有循环?@JoãoAlmeida,更常见的情况是,对象不包含对自身的引用(包括嵌套引用),您应该知道您的对象是否包含。一个包含循环的简单例子是双链接列表。谢谢你的建议。现在,我将使用封送处理而不是pickle,但我可能会再次讨论这个问题,并在将来迁移到基于db的解决方案。干杯@斯蒂芬·波莱托——这很酷,如果马尔萨尔行得通,它也行得通,这一点可以留到后世:)
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