Ускорить запрос Pymongo - PullRequest
       2

Ускорить запрос Pymongo

0 голосов
/ 18 сентября 2018

Я использую Python 3, Pymongo с mongodb 4.0 и Ifxpy для запроса базы данных Informix.У меня есть 4 Коллекции в моей базе данных MongoDB:

  • Пользователь
  • Офис
  • Домашнее животное
  • Автомобиль

ОдинУ пользователя есть один офис, один питомец и один автомобиль.Итак, у меня есть 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 секунд.

Можете ли вы сказать мне, что я делаю не так?

1 Ответ

0 голосов
/ 18 сентября 2018

Чтобы ускорить этот алгоритм, вам нужно уменьшить количество запросов MongoDB, которые вы можете сделать, используя то, что вы знаете о своих данных.Итак, если вы, например, знаете, что у вас есть только несколько отдельных офисов, или если вы все равно будете запрашивать их все на каком-то этапе (или одни и те же снова и снова), то вы, вероятно, захотите загрузить все офисы в один предварительныйвыйдите из цикла (!!!) и кэшируйте их, используя словарь для быстрого поиска внутри цикла, не требуя повторного обращения к базе данных.То же самое относится к домашним животным и автомобилям.

Итак, точнее, я бы:

  1. запустил запрос Informix так, как вы это уже делаете
  2. , чтобы получить все офисы,домашние животные и автомобили, используя три предварительных запроса.Если вы хотите оптимизировать этот этап, вам нужно получить только те, которые содержатся в различных значениях соответствующего столбца в вашем наборе данных informix.Также убедитесь, что вы добавили проекцию, включающую только поля name / model / code + _id в выводе, чтобы уменьшить количество задействованной обработки BSON
  3. , положить возвращаемое значениезначения в три словаря (name -> _id, model -> _id, code -> _id)
  4. цикл по результату informix устанавливает способ, которым вы уже это делаете
  5. для каждого пользователя в вашем наборе результатов informix добавьте модель обновления в список, где критерий выбора состоит из всех ранее собранных деталей, а часть обновления является статической
  6. снаружи (после)В цикле используется массовое обновление для обновления всех пользователей
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...