Shelve对于大字典太慢,如何提高性能?

14 投票
4 回答
17463 浏览
提问于 2025-04-16 03:02

我正在用Python存储一个表格,并且需要让它能够持久保存。

简单来说,我把这个表格存成一个字典,字典里是字符串和数字的对应关系。然后我用shelve来保存整个字典。

self.DB=shelve.open("%s%sMoleculeLibrary.shelve"%(directory,os.sep),writeback=True) 

我把writeback设置为True,因为我发现如果不这样做,系统会变得不太稳定。

在计算完成后,系统需要关闭数据库,并把数据存回去。现在这个数据库(表格)大约有540MB,但存储的时间特别长。表格一旦增长到500MB,存储时间就变得特别慢。不过我需要一个更大的表格,实际上我需要两个。

我可能使用了不太合适的持久化方式。有什么办法可以提高性能吗?

4 个回答

3

我觉得你的问题可能是因为你使用了 writeback=True。文档中提到(我强调的部分):

由于Python的特性,shelf(一个持久化字典)无法知道什么时候一个可变的字典条目被修改。默认情况下,只有在将修改后的对象赋值给shelf时,它们才会被写入(可以参考示例)。如果将可选的writeback参数设置为True,那么所有被访问的条目都会被缓存到内存中,并在调用sync()和close()时写回;这虽然让修改持久字典中的可变条目变得更方便,但如果访问了很多条目,缓存可能会消耗大量内存,并且关闭操作会变得非常慢,因为所有被访问的条目都要写回(没有办法判断哪些被访问的条目是可变的,也不知道哪些实际上被修改过)。

你可以考虑不使用 writeback=True,确保数据只写入一次(不过要注意,后续的修改会丢失)。

如果你觉得这不是合适的存储选项(没有了解数据结构很难判断),我建议使用sqlite3,它是Python内置的(所以非常便携),性能也很好。虽然它比简单的键值存储稍微复杂一些。

可以查看其他回答以获取更多替代方案。

11

根据我的经验,我建议使用SQLite3,这个工具是Python自带的。它在处理比较大的数据库和大量数据时表现很好。即使有几百万个键和几GB的数据也没问题。在这种情况下,使用Shelve就完全没必要了。而且,单独使用数据库进程也没有什么好处,只会增加上下文切换的麻烦。在我的测试中,我发现SQLite3是处理本地大数据集时的首选。运行像mongo、mysql或postgresql这样的本地数据库引擎并没有带来额外的好处,反而速度更慢。

15

如果你想存储一个很大的字典,里面有很多 字符串 : 数字 的键值对,我建议你使用一种叫做 MongoDB 的存储方案。MongoDB 是一个很棒的数据库,它的 Python 接口叫做 Pymongo,使用起来非常方便。MongoDB 本身轻量级且速度非常快,而且在 Python 中,json 对象会直接变成字典。这意味着你可以把你的 字符串 键当作对象的 ID,这样可以节省存储空间并且查找速度很快。

为了让你了解代码有多简单,看看下面的例子:

d = {'string1' : 1, 'string2' : 2, 'string3' : 3}
from pymongo import Connection
conn = Connection()
db = conn['example-database']
collection = db['example-collection']
for string, num in d.items():
    collection.save({'_id' : string, 'value' : num})
# testing
newD = {}
for obj in collection.find():
    newD[obj['_id']] = obj['value']
print newD
# output is: {u'string2': 2, u'string3': 3, u'string1': 1}

你只需要把 unicode 转换回来,这个过程非常简单。

撰写回答