对MongoDB实例中的每个文档执行操作

时间:2016-09-10 23:25:28

标签: python mongodb performance optimization

我有一个包含150万个文档的mongoDB集合,所有文档都有相同的字段,我想获取字段A的内容(在每个文档中都是唯一的)并在其上执行f(A),然后在Python中创建和填充Field B. Pseudocode:

for i in collection.find():
    x = i**2
    collection.update(i,x) #update i with x

注意:我知道更新代码可能有误,但除非它影响操作速度,否则为了简单起见,我选择将其保留在那里

问题是,这段代码真的很慢,主要是因为它可以在大约一秒内运行1000个文档,然后服务器将光标切断大约一分钟,然后它允许另外1000个。我想知道是否有什么方法可以优化这个操作,或者如果我遇到这个缓慢的瓶颈。

附加说明:

  1. 我已将batch_size调整为实验,速度更快,但效率不高,仍然需要数小时

  2. 我也知道SQL可能会更快地执行此操作,还有其他原因我使用与此问题无关的noSQL DB

  3. 该实例在本地运行,因此无论出于何种目的,都没有网络延迟

  4. 我看过this问题,但答案并没有真正解决我的问题

2 个答案:

答案 0 :(得分:0)

数据库客户端往往从实际的数据库活动中抽象出来,因此观察到的延迟行为可能具有欺骗性。在那段时间你可能真的在锤击数据库,但是这个活动在Python解释器中都是隐藏的。

也就是说,你可以采取一些措施让它更轻盈。

1)在您基于更新的属性A上添加索引。这样可以更快地返回。

2)在find电话上放置投影操作员:

for doc in collection.find(projection=['A']):

这将确保您只返回所需的字段,如果您已正确索引唯一的A属性,则会确保您的结果完全来自非常快速的索引。

3)使用更新运算符确保您只需要发回新字段。而不是发送整个文件,发回字典:

{'$set': {'B': a**2}}

将在每个文档中创建字段B,而不会影响任何其他内容。

所以,整个块看起来像这样:

for doc in collection.find(projection=['A', '_id']):
    collection.update(filter={'_id': doc['_id']},
                      update={'$set': {'B': doc['A']**2}})

这应该会大大减少Mongo必须做的工作,以及(目前与您无关)网络流量。

答案 1 :(得分:0)

也许你应该在多个线程中进行更新。我认为在一个线程中加载数据,将其拆分为多个部分并将这些部分传递给将执行更新的并行工作线程可能会更好。它会更快。

编辑:

我建议你做分页查询。 Python伪代码:

count = collection.count()
page_size = 20
i = 0;
while(i < count):
    for row in collection.find().limit(pageSize).skip(i):
        x = i**2
        collection.update(i, x);
    i += page_size