Python:存储大型数据结构

4

我目前在进行一个使用相对较大的字典(大约800MB)的Python项目。我尝试使用pickle存储其中一个字典,但是出现了MemoryError。

在Python中,正确的文件保存方式是什么?我应该使用数据库吗?


这取决于你在这些字典中存储的内容。也许json模块是一个可以轻松尝试的替代方案(import json; json.dumps(mydict)),看看是否会出现相同的错误。 - Tim Pietzcker
只需将数据转储到CSV文件中。我已经成功地为大小达3GB的文件完成了这项工作。 - user2963623
你是说你有字典值字段,每个字段都是800MB,还是所有值的总和是800MB? - user590028
用户590028 - 我的意思是整个结构,所有值的总和,为800 MB。 - Blanky
Tim Pietzcker - 我喜欢那个想法,但我认为它不会起作用,因为这些值包含一系列的frozensets。 - Blanky
5个回答

8

Python标准shelve模块为持久化对象提供了类似字典的接口。它可以与许多数据库后端一起使用,并且不受RAM限制。与直接使用数据库相比,使用shelve的优点是大部分现有代码保持不变。但这样做会以速度(与内存中的字典相比)和灵活性(与直接使用数据库相比)为代价。


我使用shelve,因为它只需要最小的代码修改。谢谢。 - Blanky

3
shelf不同的是,klepto不需要将整个字典存储在单个文件中(当您只需要一个条目时,使用单个文件进行读写非常缓慢)。此外,与shelf相比,klepto可以存储几乎任何类型的Python对象,您可以将函数、lambda、类实例、套接字、多处理队列等存储到其中。 klepto提供了一个字典抽象,用于向数据库编写内容,包括将文件系统视为数据库(即将整个字典写入单个文件,或将每个条目写入其自己的文件)。对于大型数据,我经常选择将字典表示为文件系统上的目录,并使每个条目成为一个文件。 klepto还提供了各种缓存算法(如mrulrulfu等),以帮助您管理内存中的缓存,并将使用算法来对转储和从档案后端进行加载。
>>> from klepto.archives import dir_archive
>>> d = {'a':1, 'b':2, 'c':map, 'd':None}
>>> # map a dict to a filesystem directory
>>> demo = dir_archive('demo', d, serialized=True) 
>>> demo['a']
1
>>> demo['c']
<built-in function map>
>>> demo          
dir_archive('demo', {'a': 1, 'c': <built-in function map>, 'b': 2, 'd': None}, cached=True)
>>> # is set to cache to memory, so use 'dump' to dump to the filesystem 
>>> demo.dump()
>>> del demo
>>> 
>>> demo = dir_archive('demo', {}, serialized=True)
>>> demo
dir_archive('demo', {}, cached=True)
>>> # demo is empty, load from disk
>>> demo.load()
>>> demo
dir_archive('demo', {'a': 1, 'c': <built-in function map>, 'b': 2, 'd': None}, cached=True)
>>> demo['c']
<built-in function map>
>>> 

klepto还提供了使用内存映射文件后端进行快速读写的功能。还有其他标志,例如compression,可用于进一步自定义数据存储方式。使用(MySQL等)数据库作为后端与使用文件系统一样容易(完全相同的接口)。您可以使用标志cached=False完全关闭内存缓存,并直接从磁盘或数据库中读取和写入。

>>> from klepto.archives import dir_archive
>>> # does not hold entries in memory, each entry will be stored on disk
>>> demo = dir_archive('demo', {}, serialized=True, cached=False)
>>> demo['a'] = 10
>>> demo['b'] = 20
>>> demo['c'] = min
>>> demo['d'] = [1,2,3]

获取 klepto 请前往此处:https://github.com/uqfoundation

0

也许你可以使用sqlite3?除非你的Python版本真的很老,否则它应该是可用的:https://docs.python.org/2/library/sqlite3.html

我没有检查过sqlite3的限制,并且我对它在你的情况下的有用性一无所知,但值得一试。


我考虑过,但需要对代码进行相当大的修改。不过还是谢谢你的建议。 - Blanky

0

当您将整个数据结构进行pickle时,会受到系统RAM的限制。但是,您可以分块处理。

streaming-pickle看起来是pickle文件类对象大于内存的解决方案。

https://gist.github.com/hardbyte/5955010


-1

由于它是一个字典,您可以将其转换为键值对列表 ([(k, v)])。然后,您可以使用任何技术(如 pickle)将每个元组序列化为字符串,并逐行将它们存储到文件中。这样,并行处理、检查文件内容等也更容易。

有一些库允许您使用单个对象进行流式传输,但在我看来,这只会使事情变得更加复杂。仅按行存储就可以减少很多麻烦。


我认为这个过于复杂了,因为shelve已经可以做到这一点了。 - simonzack
将东西逐行存储对我来说似乎并不复杂。如果您使用 shelve,那么您就会被绑定到 shelve 上。例如,如果您逐行存储 JSON,则几乎可以使用 Pig、Hive 等任何技术进行读取。此外,当数据变得非常大且您不需要随机访问时,DBM 文件并不是最好的选择。逐行文件易于使用 head 等工具进行检查,可以更节省空间,并且更难损坏。 - Enno Shioji

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接