MongoDB оптимизирует несколько find_one + вставка внутри цикла - PullRequest
0 голосов
/ 29 августа 2018

Я использую MongoDB 4.0.1 и Pymongo с pyhton 3.5. Я должен зацикливать более 12000 элементов каждые 30 - 60 секунд и добавлять новые данные в MongoDB. Для этого примера мы поговорим о User, Pet и Car. Пользователь может получить 1 машину и 1 питомца.

Мне нужен ObjectID питомца и ObjectID автомобиля, чтобы создать моего пользователя, поэтому я должен добавить их по одному в цикле, и это очень медленно. Требуется ~ 25 секунд, чтобы найти существующие данные и добавить их, если данные не существуют.

while dictionary != False:
    # Create pet if not exist
    existing_pet = pet.find_one({"code": dictionary['pet_code']})

    if bool(existing_pet):
        pet_id = existing_pet['_id']
    else:
        pet_id = pet.insert({
            "code" : dictionary['pet_code'],
            "name" : dictionary['name']
        })
        # Call web service to create pet remote

    # Create car if not exist
    existing_car = car.find_one({"platenumber": dictionary['platenumber']})

    if bool(existing_car):
        car_id = existing_car['_id']
    else:
        car_id = car.insert({
            "platenumber" : dictionary['platenumber'],
            "model" : dictionary['model'],
            "energy" : 'electric'
        })
        # Call web service to create car remote

    # Create user if not exist
    existing_user = user.find_one(
        {"$and": [
            {"user_code": dictionary['user_code']},
            {"car": car_id},
            {"pet": pet_id}
        ]}
    )

    if not bool(existing_user):
        user_data.append({
            "pet" : pet_id,
            "car" : car_id,
            "firstname" : dictionary['firstname'],
            "lastname" : dictionary['lastname']
        })
        # Call web service to create user remote

# Bulk insert user
if user_data:
    user.insert_many(user_data)

Я создал индексы для каждого столбца, используемого для find_one:

db.user.createIndex( { user_code: 1 } )
db.user.createIndex( { pet: 1 } )
db.user.createIndex( { car: 1 } )
db.pet.createIndex( { pet_code: 1 }, { unique: true }  )
db.car.createIndex( { platenumber: 1 }, { unique: true }  )

Есть способ ускорить этот цикл? Есть что-то с агрегацией или что-то еще, чтобы помочь мне? Или, может быть, другой способ сделать то, что я хочу?

Я открыт для всех советов.

1 Ответ

0 голосов
/ 31 августа 2018

Не выполняйте 12000 запросов find_one, выполните 1 запрос, чтобы собрать все, что существует, с оператором $ in. Код будет что-то вроде:

pet_codes = []
pet_names = []
while dictionary != False:
    pet_codes.append(dictionary['pet_code'])
    pet_names.append(dictionary['pet_name'])

pets = dict()
for pet in pet.find({"code": {$in: pet_codes}}):
    pets[pet['code']] = pet

new_pets = []
for code, name in zip(pet_codes, pet_names):
    if code not in pets:
        new_pets.add({'pet_code': code, 'name': name})

pet.insert_many(new_pets)

Поскольку у вас уже есть индекс pet_code, делающий его уникальным, мы можем сделать лучше: просто попробуйте вставить их все, потому что, если мы попытаемся вставить существующий, эта запись получит ошибку, но остальное будет успешно выполнено с помощью упорядоченный = False от документов :

new_pets = []
while dictionary != False:
    new_pets.add({
        "code" : dictionary['pet_code'],
        "name" : dictionary['name']
    })
pet.insert_many(new_pets, ordered=False)

В случае, если у вас нет уникального набора ограничений, другим методом является пакетирование операций

...