【问题标题】:Speed up Pymongo query加快 Pymongo 查询
【发布时间】:2018-09-19 00:11:00
【问题描述】:

我正在使用 Python 3、带有 mongodb 4.0 的 Pymongo 和 Ifxpy 来查询 Informix 数据库。我的 MongoDB 数据库中有 4 个集合:

  • 用户
  • 办公室
  • 宠物
  • 汽车

一个用户有一个办公室,一个宠物和一辆汽车。所以我对 User 集合中的每个字段都有 3 个引用。

我需要这样的东西:

  • 我想查找是否有名称为John 的用户和名称为Mickey 的宠物以及模型为Tesla 和状态为inactive 的汽车。之后,我将用户状态更新为active。我必须查询 Office,但在此示例中我没有使用它。

我为每个字段创建了索引:

office.create_index([("code", pymongo.DESCENDING)], unique=True)
pet.create_index([("name", pymongo.DESCENDING)], unique=True)
car.create_index([("model", pymongo.DESCENDING)], unique=True)
user.create_index([("username", pymongo.DESCENDING)], unique=True)
user.create_index([("pet", pymongo.DESCENDING)])
user.create_index([("car", pymongo.DESCENDING)])
user.create_index([("status", pymongo.DESCENDING)])

这是我的代码:

office_id = None
car_id = None
pet_id = None
ifx_connection = IfxPy.connect(ifx_param, "", "")
stmt = IfxPy.exec_immediate(ifx_connection, sql)
dictionary = IfxPy.fetch_assoc(stmt) # Get data key / value
start = time.time()

# Loop on informix data (20 000 items)
while dictionary != False:
    # Trim all string in dict
    dictionary = {k: v.strip() if isinstance(v, str) else v for k,v in dictionary.items()}

    # Get office
    office_code = dictionary['office_code']
    existing_office = office.find_one({"code": office_code})

    if bool(existing_office):
        office_id = existing_office['_id']

    # Get pet
    existing_pet = pet.find_one({"name": dictionary['pet_name']})
    if bool(existing_pet):
        pet_id = existing_pet['_id']

    # Get car
    existing_car = car.find_one({"model": dictionary['car_model']})

    if bool(existing_car):
        car_id = existing_car['_id']

    # Get user
    existing_user = user.find_one({
        "username": dictionary['username'],
        "car": car_id,
        "pet": pet_id,
        "status" : "inactive"
    })

    if bool(existing_user):
        # Change user status
        user.update_one({'_id': existing_user['_id']}, {"$set": {"status" : "active"}}, upsert=False)

    # Next row
    dictionary = IfxPy.fetch_assoc(stmt)

如果我从循环中删除 MongoDB 代码,则需要 1.33 秒。如果我查询 MongoDB,它需要 47 秒。我有 20 000 件物品。我认为它真的很慢。

我试图通过删除所有 find_one 并只保留一个来查看每个带有 start = time.time() 的 find_one 的时间。如果我只让 Office find_one 它需要大约 12 秒,另一个相同。如果我让客户 find_one 也需要大约 12 秒。所以 ~12 * 4 这就是为什么所有 find_one 需要 ~47 秒。

你能告诉我我做错了什么吗?

【问题讨论】:

  • 索引集合以支持您的查询。
  • 对不起,我已经有索引了。我会编辑
  • 抱歉,刚刚意识到您对每个集合执行 20k 次查询,这相当于每个查询半毫。我怀疑除非您更改应用程序,否则您是否可以使其更快。例如。一次将所有办公室加载到本地列表中,然后在不接触 mongo 的情况下检查它是否存在。
  • 我不能这样做,因为如果您观看用户的 find_one,我需要检查多个属性:用户名 car_id 和 pet_id
  • 我提出这个问题是因为@dnickless 让我解释我的循环:stackoverflow.com/a/52371589/2519631 因为等待 47 秒是不正常的

标签: python mongodb optimization


【解决方案1】:

要加快该算法的速度,您需要通过利用您对数据的了解来减少可以执行的 MongoDB 查询的数量。所以,如果你例如知道您只有几个不同的办公室,或者如果您可能在某个阶段(或一遍又一遍)查询所有办公室,那么您可能希望在循环之外的一个初步步骤中加载所有办公室(!!!) 并使用字典缓存它们,以便在循环内快速查找,而无需另一个数据库往返。宠物和汽车也是如此。

所以,更准确地说,我会:

  1. 按照您的方式运行 informix 查询
  2. 使用三个预先查询检索所有办公室、宠物和汽车。如果您想优化此阶段,您只需检索informix 数据集中各个列的不同值中包含的值。还要确保添加投影以仅在输出中包含 name/model/code + _id 字段,以减少所涉及的 BSON 处理量
  3. 将返回值放入三个字典中(name->_id,model->_id,code->_id
  4. 按照您已经完成的方式循环通过 informix 结果集
  5. 为您的 informix 结果集中的每个用户附加一个更新模型到一个列表,其中选择标准由所有先前收集的详细信息组成,并且更新部分是静态的
  6. 在循环之外(之后)使用bulk update 更新所有用户

【讨论】:

  • 非常感谢您的支持,您帮了我很多
猜你喜欢
  • 2013-03-07
  • 2010-12-17
  • 1970-01-01
  • 2020-05-03
  • 2020-10-07
  • 2013-03-29
  • 2018-11-07
  • 1970-01-01
相关资源
最近更新 更多