用Python将庞大的Mongo结果集以资源友好的方式写入磁盘
有一个Mongo数据库,里面有超过500万条记录。我需要获取所有这些记录中某个特定属性的“表示”,可以存放在一个变量里,或者写到磁盘上的文件中,随便什么都行。
我的查询大概是这样的:
cursor = db.collection.find({"conditional_field": {"subfield": True}}, {"field_i_want": True})
我第一次尝试的办法是把'cursor'进行Pickle处理,但我很快意识到这样不行。
在这个情况下,“field_i_want”是一个整数。作为我尝试过的一个例子,我这样做了,结果几乎让服务器卡了好几分钟:
ints = [i['field_i_want'] for i in cursor]
...只是为了获取这些整数的列表。这让服务器的CPU资源占用过高,持续了太久。
有没有什么简单的方法可以把这些结果放到列表、元组、集合、Pickle、文件里,或者其他什么地方,而不会让CPU完全占满呢?
理想情况下,我希望能把结果保存下来,以便以后再读取。但我希望在保存的时候尽量对服务器友好。
2 个回答
虽然这个问题已经被接受了,但我想补充一点,你可能还需要考虑添加一个索引。我们很容易就觉得MongoDB的“带宽”用尽了,但它叫“mongo”是有原因的!根据你数据库的结构,500万条响应可能运行得非常快;听起来你的数据总共大约是500万整数?为了简单起见,我们假设field_i_want等是存储字段名称的变量。如果你这样做:
db.collection.ensure_index([(conditional_field, DESCENDING), (field_i_want, ASCENDING)])
比如说,你就能执行一个“覆盖查询”,像这样:
db.collection.find({conditional_field:True},fields={field_i_want:1, _id:-1})
有时候,pymongo会随意决定需要把MongoDB的字典语法转换成列表,就像上面提到的ensure_index和fields那样。我相信你可以为字段使用字典,这对于覆盖查询是必要的,但如果不行,你就需要研究一下如何用列表的复杂语法来做覆盖查询。覆盖查询的重要点是只返回你索引中的字段。所以你不想要“_id”,因为虽然“_id”是自动索引的,但它不是将要使用的索引的一部分。执行覆盖查询时,查询几乎没有时间消耗。它会立即把你想要的所有数据交给你。如果你想要的是一个列表而不是字典(“文档”)的列表,你可以直接拿到响应,然后做类似这样的操作:
[y for x,y in myquery.items()]
Mongo本身就是一种二进制表示,存储能力很强,所以这可能是那种问题,最好的答案是不断完善问题。如果你只是想要一个数据转储,你可以使用Mongo附带的工具,这些工具可以在你的mongod二进制文件所在的同一目录中找到。这将允许你把数据导出为json格式,存储为文件(不过现在它是以bson格式存储的文件)。
我觉得把结果逐步输出可能会有帮助:
with open("/path/to/storage/file", "w") as f:
for row in cursor:
f.write(row['your_field'])
如果不必要的话,就不要把所有东西都放在内存里。