Python + MongoDB - 光标迭代太慢 - 未解决?

Posted

技术标签:

【中文标题】Python + MongoDB - 光标迭代太慢 - 未解决?【英文标题】:Python + MongoDB - Cursor iteration too slow - Unresolved? 【发布时间】:2013-08-25 09:06:29 【问题描述】:

我有一个数据库查找查询,它返回 150k 个文档,其中每个文档包含三个整数字段和一个日期时间字段。以下代码尝试从游标对象创建列表。迭代光标非常慢 - 大约 80 秒!通过 C++ 驱动程序执行相同的操作要快几个数量级——这一定是 PyMongo 的问题?

client = MongoClient()
client = MongoClient('localhost', 27017)
db = client.taq
collection_str = "mycollection"
db_collection = db[collection_str]

mylist = list(db_collection.find())

这个问题之前已经讨论过,我尝试了这些建议。一种是更改默认批量大小。所以我尝试了以下方法:

cursor = db_collection.find()
cursor.bath_size(10000)
mylist = list(cursor)

但是,这没有影响。第二个建议是检查是否安装了 C 扩展 - 我已经安装了它们,所以这不是问题。 Mongo 数据库安装在同一台机器上,因此它不是网络问题 - 它可以在 C++ 中正常工作......从 Pymongo 查询是问题。

既然 MongoDB 被宣传为能够处理大数据,那么肯定有一种方法可以通过 Python 快速检索数据吗?这个问题之前已经提出过,但我还没有找到解决方案....有没有人提出有效的建议?在这种情况下,我正在检索 150k 文档,但通常查询将检索 100 万,所以这对我来说将是一个真正的问题。

谢谢。

【问题讨论】:

你能提供一个样本文件吗? 150k 个文档有多少数据? 1 MB 还是 2.4 GB?文件有多复杂?您可以尝试连接到套接字而不是通过 tcp - 这会提高性能吗?另外为什么要转换为列表?在使用它之前,您需要等到所有数据都通过网络传输。除了python,你还有多少内存?在集合上运行touch 来加热缓存是否会提高性能?最好向jira.mongodb.org/browse/PYTHON 添加票证,但演示用例将有助于调试 每个文档都非常简单,包含一个日期时间字段和三个整数值字段。我正在转换为列表,以便可以将列表传递给 Pandas DataFrame 的构造函数。忽略列表转换,如果我删除这一行并只迭代光标,它仍然很慢。 RAM 不是问题,这台服务器上有 30GB 的 RAM。我发布的代码足以重现问题。尝试迭代 150k 简单文档的游标,看看需要多长时间。 遍历光标的结果需要多长时间,但不将项目放入列表中?几乎一样? @Rob,在您的代码中,光标没有迭代。事实上,如果你从列表中创建 DataFrame,你也不需要迭代。那么,你到底是什么意思? 这个 Rob 有什么更新吗? 【参考方案1】:

我无法复制 - 我正在加载 150k 文档并在 ~0.5 > ~0.8 秒内转换为列表。以下是 timeit 测试脚本的结果 - 以秒为单位将 150,000 个文档从数据库转换为列表。

--------------------------------------------------
Default batch size
0.530369997025
--------------------------------------------------
Batch Size 1000
0.570069074631
--------------------------------------------------
Batch Size 10000
0.686305046082

这是我的测试脚本:

#!/usr/bin/env python

import timeit

def main():
    """
    Testing loading 150k documents in pymongo
    """

    setup = """
import datetime
from random import randint
from pymongo import MongoClient
connection = MongoClient()
db = connection.test_load
sample = db.sample
if db.sample.count() < 150000:
    connection.drop_database('test_load')

    # Insert 150k sample data
    for i in xrange(15000):
        sample.insert(["date": datetime.datetime.now(),
                        "int1": randint(0, 1000000),
                        "int2": randint(0, 1000000),
                        "int4": randint(0, 1000000) for i in xrange(10)])

"""

    stmt = """
from pymongo import MongoClient
connection = MongoClient()

db = connection.test_load
sample = db.sample

cursor = sample.find()
test = list(cursor)
assert len(test) == 150000
"""

    print "-" * 100
    print """Default batch size"""
    t = timeit.Timer(stmt=stmt, setup=setup)
    print t.timeit(1)

    stmt = """
from pymongo import MongoClient
connection = MongoClient()

db = connection.test_load
sample = db.sample

cursor = sample.find()
cursor.batch_size(1000)
test = list(cursor)
assert len(test) == 150000
"""

    print "-" * 100
    print """Batch Size 1000"""
    t = timeit.Timer(stmt=stmt, setup=setup)
    print t.timeit(1)

    stmt = """
from pymongo import MongoClient
connection = MongoClient()

db = connection.test_load
sample = db.sample

cursor = sample.find()
cursor.batch_size(10000)
test = list(cursor)
assert len(test) == 150000
"""

    print "-" * 100
    print """Batch Size 10000"""
    t = timeit.Timer(stmt=stmt, setup=setup)
    print t.timeit(1)

if __name__ == "__main__":
    main()

我很困惑您是如何获得 80 秒而不是 0.8 秒的!我根据您的定义创建了示例数据 - 这与您的有何不同?

【讨论】:

【参考方案2】:

如果您要返回集合中的每个项目(相对于通过某些字段查询),不确定这是否会有所帮助,但您是否尝试过为字段创建索引?

db_collection.create_index([("field_name", pymongo.ASCENDING)])
db_collection.reindex()

文档:https://api.mongodb.org/python/current/api/pymongo/collection.html

【讨论】:

以上是关于Python + MongoDB - 光标迭代太慢 - 未解决?的主要内容,如果未能解决你的问题,请参考以下文章

光标在 Xcode 中移动太慢

PyMongo 光标迭代

聚合中的mongodb $count 太慢,超过1mill。数据库中的文档

Mongodb查询对索引字段的部分字段搜索来说太慢了

MongoDB游标在Perl中散列

mongodb3.4.4安装副本集,wt引擎配置优化