如何在Python中使用不适合内存的大型dicts?

时间:2013-12-19 15:45:57

标签: python memory data-structures dictionary

我们使用dict包含大约4GB的数据进行数据处理。这很方便快捷。

我们遇到的问题是这个dict可能超过32GB。

我正在寻找一种方法来使用dict(就像变量与get() - 方法等),它可能比可用内存更大。如果dict以某种方式将数据存储在磁盘上并在调用get(key)时从磁盘检索数据并且key的值不在内存中,那就太棒了。

最好我不想使用外部服务,比如SQL数据库。

我确实找到Shelve,但似乎也需要记忆。

有关如何解决此问题的任何想法?

5 个答案:

答案 0 :(得分:3)

听起来你可以使用一个键值存储,它目前在No-SQL的流行语中大肆宣传。关于它的好介绍可以在例如

中找到

http://ayende.com/blog/4449/that-no-sql-thing-key-value-stores

它只是一个包含您描述的API的数据库。

答案 1 :(得分:1)

如果您不想使用SQL数据库(这是解决此类问题的合理解决方案),您必须找出压缩您正在使用的数据的方法或使用{{3 (或你自己)自己做光盘映射。

您还可以查看a library like this one了解更多策略。

答案 2 :(得分:1)

使用pickle模块将字典序列化为磁盘。然后从字典的迭代器中获取连续值,并将它们放入缓存中。然后实现缓存方案,如LRU;使用字典的`popitem()方法删除字典项,并在LRU的情况下添加以前访问过的项。

答案 3 :(得分:1)

我找不到任何(快速)模块来做这个并决定创建我自己的(我的第一个python项目 - 感谢@blubber的一些想法:P)。你可以在GitHub上找到它:https://github.com/ddofborg/diskdict欢迎评论!

答案 4 :(得分:0)

/编辑:这是一个Python模块:fdict

我有类似的问题,但我正在研究嵌套的dicts。由于我的数据集具有非常嵌套的特性,所有可用的解决方案都不适合我的用例:shelvechestshovesqlite,{{1}并且,任何其他NoSQL数据库主要在你有一个级别时工作,因为它们都将pickle / JSON值序列化到数据库中,因此第一级之后的所有值都被序列化,并且不可能对嵌套对象进行增量更新(你必须从第一级节点加载整个分支,修改,然后再将其放回),如果你的嵌套对象本身很大,这是不可能的。

这意味着当您拥有大量第一级节点时,这些解决方案将有所帮助,但是当您拥有一些第一级节点和深层次结构时,这些解决方案将无济于事。

为了解决这个问题,我在本机Python dict上创建了一个自定义dict类,在内部以扁平形式表示嵌套dict:zodb将在内部表示为dict()['a']['b']

然后在这个"内部扁平" dict,您现在可以使用任何对象到磁盘解决方案,如dict()['a/b'],这是我使用的,但您可以轻松替换其他内容。

以下是代码:

shelve

import shelve class fdict(dict): '''Flattened nested dict, all items are settable and gettable through ['item1']['item2'] standard form or ['item1/item2'] internal form. This allows to replace the internal dict with any on-disk storage system like a shelve's shelf (great for huge nested dicts that cannot fit into memory). Main limitation: an entry can be both a singleton and a nested fdict, and there is no way to tell what is what, no error will be shown, the singleton will always be returned. ''' def __init__(self, d=None, rootpath='', delimiter='/', *args): if d: self.d = d else: self.d = {} self.rootpath = rootpath self.delimiter = delimiter def _buildpath(self, key): return self.rootpath+self.delimiter+key if self.rootpath else key def __getitem__(self, key): # Node or leaf? if key in self.d: # Leaf: return the value return self.d.__getitem__(key) else: # Node: return a new full fdict based on the old one but with a different rootpath to limit the results by default return fdict(d=self.d, rootpath=self._buildpath(key)) def __setitem__(self, key, value): self.d.__setitem__(self._buildpath(key), value) def keys(self): if not self.rootpath: return self.d.keys() else: pattern = self.rootpath+self.delimiter lpattern = len(pattern) return [k[lpattern:] for k in self.d.keys() if k.startswith(pattern)] def items(self): # Filter items to keep only the ones below the rootpath level if not self.rootpath: return self.d.items() else: pattern = self.rootpath+self.delimiter lpattern = len(pattern) return [(k[lpattern:], v) for k,v in self.d.items() if k.startswith(pattern)] def values(self): if not self.rootpath: return self.d.values() else: pattern = self.rootpath+self.delimiter lpattern = len(pattern) return [v for k,v in self.d.items() if k.startswith(pattern)] def update(self, d2): return self.d.update(d2.d) def __repr__(self): # Filter the items if there is a rootpath and return as a new fdict if self.rootpath: return repr(fdict(d=dict(self.items()))) else: return self.d.__repr__() def __str__(self): if self.rootpath: return str(fdict(d=dict(self.items()))) else: return self.d.__str__() class sfdict(fdict): '''A nested dict with flattened internal representation, combined with shelve to allow for efficient storage and memory allocation of huge nested dictionnaries. If you change leaf items (eg, list.append), do not forget to sync() to commit changes to disk and empty memory cache because else this class has no way to know if leaf items were changed! ''' def __init__(self, *args, **kwargs): if not ('filename' in kwargs): self.filename = None else: self.filename = kwargs['filename'] del kwargs['filename'] fdict.__init__(self, *args, **kwargs) self.d = shelve.open(filename=self.filename, flag='c', writeback=True) def __setitem__(self, key, value): fdict.__setitem__(self, key, value) self.sync() def get_filename(self): return self.filename def sync(self): self.d.sync() def close(self): self.d.close() fdict都可以像标准sfdict一样使用(但我没有实现所有方法)。

完整代码在这里: https://gist.github.com/lrq3000/8ce9174c1c7a5ef546df1e1361417213

这是在完整的Python模块中进一步开发的:fdict

在进行基准测试后,使用间接访问(即dict)时,这比dict慢约10倍,使用直接访问(即x['a']['b']['c'])时速度约为快,尽管我在这里做不考虑x['a/b/c']保存到shelve文件的费用,仅anydbm数据结构与fdict相比。