Python shelve 内存溢出错误
我有一些数据存储在数据库里,想要处理这些数据。但是数据库的访问速度非常慢,所以我决定在处理之前先把所有数据加载到一个字典里。不过,由于存储的数据量太大,我遇到了内存不足的错误(我看到使用了超过2GB的内存)。于是我决定使用一种磁盘数据结构,发现使用shelve是一个不错的选择。以下是我做的事情(伪代码)
def loadData():
if (#dict exists on disk):
d = shelve.open(name)
return d
else:
d = shelve.open(name, writeback=True)
#access DB and write data to dict
# d[key] = value
# or for mutable values
# oldValue = d[key]
# newValue = f(oldValue)
# d[key] = newValue
d.close()
d = shelve.open(name, writeback=True)
return d
我有几个问题:
1) 我真的需要writeBack=True吗?它有什么作用?
2) 我仍然遇到内存不足的异常,因为我没有控制数据何时写入磁盘。我该怎么做?我尝试每隔几次迭代调用一次sync(),但这也没有帮助。
谢谢!
2 个回答
使用 sqlite3
模块可能是你最好的选择。其实你可以完全在内存中使用 sqlite,因为它占用的内存可能比使用 Python 对象要小一些。总的来说,使用 sqlite3
比使用 shelve
更好;因为 shelve
底层是用 pickle
实现的,这通常不是你想要的。
其实,你完全可以把你现有的数据库转换成 sqlite 数据库。sqlite 既简单又快速。
writeback=True
这个设置会让储存空间(shelf)在内存中保留你获取过的任何项目,并在储存空间关闭时将它们写回去。所以,这样会占用更多的内存,并且关闭时会变得更慢。
这个参数的好处在于,使用它后,你不需要在处理可变对象时写复杂的代码,比如当你用方法来修改这些对象时 -- 只需要
shelf['foobar'].append(23)
就可以了(前提是shelf
是以开启writeback的方式打开的),假设键'foobar'
对应的项目是一个列表。当然,如果shelf
是以不启用writeback的方式打开的,那么这个操作就不会有任何效果(也就是说,磁盘上的项目不会改变) -- 在这种情况下,你实际上需要按照你评论中的方式来编写代码
thelist = shelf['foobar']
thelist.append(23)
shekf['foobar'] = thelist
这就有点让人失望了,因为风格上不太好看。
不过,既然你确实遇到了内存问题,我强烈建议不要使用这个不太可靠的writeback选项。我觉得可以称它为“不太可靠”,因为我是最早提出并实现这个功能的人,但那是很多年前的事了,现在我大部分时间都在后悔这样做 -- 它带来的困惑(就像你的问题所显示的那样)远远超过了它在将原本为字典编写的代码(这类代码会使用第一种方式,而不是第二种,因此需要重写才能在储存空间中使用而不出错)时所带来的优雅和便利。唉,抱歉,当时这确实看起来是个好主意。