【问题标题】:How do I get improved pymongo performance using threading?如何使用线程提高 pymongo 性能?
【发布时间】:2016-11-21 08:58:04
【问题描述】:

我正在尝试查看 pymongo 的性能改进,但我没有观察到任何改进。

我的示例数据库有 400,000 条记录。从本质上讲,我看到线程和单线程的性能是相同的 - 唯一的性能提升来自多进程执行。

pymongo 在查询期间不会释放 GIL 吗?

单次性能:真实0m0.618s

Multiproc:real 0m0.144s

多线程:real 0m0.656s

常规代码:

choices = ['foo','bar','baz']


def regular_read(db, sample_choice):
    rows = db.test_samples.find({'choice':sample_choice})
    return 42  # done to remove calculations from the picture

def main():
    client = MongoClient('localhost', 27017)
    db = client['test-async']
    for sample_choice in choices:
        regular_read(db, sample_choice)

if __name__ == '__main__':
    main()

$ time python3 mongotest_read.py 

real    0m0.618s
user    0m0.085s
sys 0m0.018s

现在,当我使用多处理时,我可以看到一些改进。

from random import randint, choice

import functools
from pymongo import MongoClient
from concurrent import futures

choices = ['foo','bar','baz']
MAX_WORKERS = 4

def regular_read(sample_choice):
    client = MongoClient('localhost', 27017,connect=False)
    db = client['test-async']
    rows = db.test_samples.find({'choice':sample_choice})
    #return sum(r['data'] for r in rows)
    return 42

def main():
    f = functools.partial(regular_read)
    with futures.ProcessPoolExecutor(MAX_WORKERS) as executor:
        res = executor.map(f, choices)

    print(list(res))
    return len(list(res))

if __name__ == '__main__':
    main()

$ time python3 mongotest_proc_read.py 
[42, 42, 42]

real    0m0.144s
user    0m0.106s
sys 0m0.041s

但是当您从 ProcessPoolExecutor 切换到 ThreadPoolExecutor 时,速度会下降到单线程模式。

...

def main():
    client = MongoClient('localhost', 27017,connect=False)
    f = functools.partial(regular_read, client)
    with futures.ThreadPoolExecutor(MAX_WORKERS) as executor:
        res = executor.map(f, choices)

    print(list(res))
    return len(list(res))

$ time python3 mongotest_thread_read.py 
[42, 42, 42]

real    0m0.656s 
user    0m0.111s
sys 0m0.024s

...

【问题讨论】:

  • 我也试过给每个线程它自己的 MongoClient - 结果是一样的。

标签: multithreading performance pymongo


【解决方案1】:

PyMongo 使用标准 Python 套接字模块,该模块在通过网络发送和接收数据时会丢弃 GIL。但是,瓶颈不是 MongoDB 或网络:而是 Python。

CPU 密集型 Python 进程无法通过添加线程进行扩展;实际上,由于上​​下文切换和其他低效率,它们会稍微放慢速度。要在 Python 中使用多个 CPU,请启动子进程。

我知道“查找”应该占用 CPU 似乎并不直观,但 Python 解释器的速度足以与我们的直觉相矛盾。如果查询速度很快,并且本地主机上的 MongoDB 没有延迟,那么 MongoDB 可以轻松胜过 Python 客户端。您刚刚运行的实验,用子进程代替线程,确认 Python 性能是瓶颈。

为确保最大吞吐量,请确保您启用了 C 扩展:pymongo.has_c() == True。有了这些,PyMongo 的运行速度与 Python 客户端库可以达到的速度一样快,从而获得更多的多处理吞吐量。

如果您预期的实际场景涉及更耗时的查询,或者具有一些网络延迟的远程 MongoDB,则多线程可能会给您带来一些性能提升。

【讨论】:

  • 线程中的大量 CPU 工作是将 mongo bson 转换为 dict 对象。在使用线程时,使用document_class=RawBSONDocument 会(在一定程度上)克服这个瓶颈。
猜你喜欢
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多