Pymongo 3.0.2 比 2.8.1 慢

Pymongo 3.0.2 slower than 2.8.1

我正在 运行在 MongoDB 上安装一个简单的 Django 应用程序,并且最近升级到 PyMongo 3.0.2 —— 但是这个 运行 速度非常慢。如果我降级到 PyMongo 2.8.1 或 2.7.2,它会再次加速。 MongoDB 3 和 2.6 都会发生这种情况,所以我认为一些基本的东西已经改变了。根据变更日志,PyMongo 3 实际上应该加快很多,而且我找不到任何会导致性能下降的明显变化。我在 SO 或 Google 上没有发现相关问题。这是 Django 1.6.4 和 Python 2.7.5.

很难举出一个这样的代码示例,但我们使用的是单个 MongoDB 实例(没有分片,没有远程主机),并且在我们的每个方法中都使用了 mongo_client,我们在方法的末尾调用close()。如果我们不断关闭连接,是否有一些新的连接重新打开行为可能会降低客户端的速度?示例方法如下:

    from pymongo import MongoClient
    mongo_client = MongoClient()        
    collection = mongo_client[self._db_prefix + 'assessment']['Assessment']
    if collection.find({'itemIds': str(item_id)}).count() != 0:
        raise errors.IllegalState('this Item is being used in one or more Assessments')
    collection = mongo_client[self._db_prefix + 'assessment']['Item']
    item_map = collection.find_one({'_id': ObjectId(item_id.get_identifier())})
    if item_map is None:
        raise errors.NotFound()
    objects.Item(item_map, db_prefix=self._db_prefix, runtime=self._runtime)._delete()
    delete_result = collection.delete_one({'_id': ObjectId(item_id.get_identifier())})
    if delete_result.deleted_count == 0:
        raise errors.NotFound()
    mongo_client.close()

更新 1:

按照建议,我使用 timeit 库创建了一个专用负载测试。使用 PyMongo 3.0.2:

timeit.timeit('MongoClient()["test_blah"]["blah"].insert_one({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient")

实际抛出错误:

  File "~/Documents/virtual_environments/assessments/lib/python2.7/site-packages/pymongo/pool.py", line 58, in _raise_connection_failure
raise AutoReconnect(msg)
AutoReconnect: localhost:27017: [Errno 49] Can't assign requested address

然后我降级到 PyMongo 2.8.1:

pip install pymongo==2.8.1

和运行相同的命令在python shell:

timeit.timeit('MongoClient()["test_blah"]["blah"].insert({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient")
8.372910976409912

这次它真的完成了...所以看起来新的 insert_one 方法做了一些不同的事情,它没有关闭连接?

更新2(含解决方案):

Bernie 的回答以及这个 SO question 帮助我们指明了正确的方向。除了使用单个 MongoClient(),我们的问题是我们在每个方法结束时关闭连接。下面的示例 timeits(均为 PyMongo 3.0.2):

>>> timeit.timeit('client["test_blah"]["blah"].insert_one({"foo":"bar"}); client.close()', number=10, setup="from pymongo import MongoClient; client=MongoClient()")
4.520946025848389
>>> timeit.timeit('client["test_blah"]["blah"].insert_one({"foo":"bar"})', number=10, setup="from pymongo import MongoClient; client=MongoClient()")
0.004940986633300781

手动关闭客户端是性能杀手...慢 1000 倍。可能是 Bernie 提到的监控线程关闭缓慢造成的?

我认为您遇到的问题是由于 MongoClient 产生了一个后台监控线程。这是 PyMongo 3.0 中的新功能,与 PyMongo 2.x 中 MongoReplicaSetClient 的行为相匹配。您应该能够通过仅生成一个 MongoClient 实例(这是使用 MongoClient 的首选方式)来加快速度。

>>> import timeit
>>> timeit.timeit('client["test_blah"]["blah"].insert_one({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient; client = MongoClient()")
2.2610740661621094
>>> import pymongo
>>> pymongo.version
'3.0.2'

>>> timeit.timeit('client["test_blah"]["blah"].insert({"foo":"bar"})', number=10000, setup="from pymongo import MongoClient; client = MongoClient()")
2.3010458946228027
>>> import pymongo
>>> pymongo.version
'2.8.1'

我还认为监控线程关闭的时间太长,我会研究解决该问题的方法。