производительность pymongo find_one_and_update на миллионах вставок / обновлений - PullRequest
0 голосов
/ 04 февраля 2019

Обновление: Если вы прочитали это, чтобы улучшить скорость вставки / обновления, проверьте, включены ли в вашей системе расширения pymongo C, запустив сначала pymongo.has_c() из консоли python.Если он разрешается до False, вам нужно либо скомпилировать pymongo с расширениями C, либо выполнить pip install --upgrade pymongo

. Это улучшило мой рабочий процесс с 17 секунд в строках 10K до примерно 0,57 секунд.

У меня есть тысячи текстовых файлов, содержащих миллионы строк данных, которые я пытаюсь импортировать в коллекцию mongodb.

В настоящее время я использую следующее def :

import re, pymongo
coll = pymongo.MongoClient().database.collection
rx = re.compile(r'[:; ]')
rx_email = re.compile(r'\S+@\S+\.\S+$')

def parser(path):
    with open(path, "rb") as f:
        for line in f:
            try:
                fields = rx.split(line.decode('utf-8'))
                email = ''
                username = ''
                for field in fields:
                    if rx_email.match(field):
                        email = field
                    elif field != fields[-1]:
                        username = field
                password = fields[-1]
                if email:
                    coll.find_one_and_update({'email': email}, {'$addToSet': {'passwords': password}}, upsert=True)
                elif username:
                    coll.find_one_and_update({'username': username}, {'$addToSet': {'passwords': password}}, upsert=True)
                else:
                    pass
            except UnicodeDecodeError:
                pass

if __name__ == "__main__":
    parser('path/to/file.txt')

Когда я пытаюсь запустить скрипт для файла с 10K строк, это заняло 74,58974479999999 секунд.Я предполагаю, что это связано с количеством элементов, с которыми MongoDB должен соответствовать, когда я вставляю?Выполнение того же цикла без взаимодействия с БД заняло 0,022998 секунд.

EDIT : как указано в Fast or Bulk Upsert в pymongo , я также пытался использовать UpdateOneс bulk_write следующим образом:

def parser(path):
    ops = []
    with open(path, "rb") as f:
        for line in f:
            if (len(ops) == 1000):
                LOCAL_DB.bulk_write(ops, ordered=False)
                ops = []
            try:
                fields = rx.split(line.decode('utf-8'))
                email = ''
                username = ''
                for field in fields:
                    if rx_email.match(field):
                        email = field
                    elif field != fields[-1]:
                        username = field
                password = fields[-1]
                if email:
                    pass
                    ops.append((UpdateOne({'identifier': email}, {'$addToSet': {'passwords': password}}, upsert=True)))
                elif username:
                    pass
                    ops.append((UpdateOne({'identifier': username}, {'$addToSet': {'passwords': password}}, upsert=True)))
                else:
                    pass
            except UnicodeDecodeError:
                pass

Время завершения 10К строк составляет 17 секунд, что, однако, является способом замедления для количества файлов и строк, которые я пытаюсь обновить.

Есть ли лучшие (и, надеюсь, более быстрые) способы сделать это?

Некоторые требования:

  1. адрес электронной почты и / или имя пользователя должны быть уникальными.
  2. Массив, содержащийпароли должны указывать каждый пароль только один раз (также уникальный).
  3. 1M строк должно (если возможно) занять менее 1 минуты для вставки.

1 Ответ

0 голосов
/ 04 февраля 2019

Кажется, мне удалось, под руководством @JohnnyHK в комментариях, получить мое начальное upsert время от ~ 74 до ~ 0,5 секунд для строк 10K, выполнив следующие действия для моего исходного кода:

import re, pymongo
rx = re.compile(r'[:; ]')
rx_email = re.compile(r'\S+@\S+\.\S+$')

def parse(path):
    ops = []
    with open(path, "rb") as f:
        for line in f:
            if (len(ops) == 1000):
                pymongo.MongoClient().database.collection.bulk_write(ops, ordered=False)
                ops = []
            try:
                fields = rx.split(line.decode('utf-8'))
                email = ''
                username = ''
                for field in fields:
                    if rx_email.match(field):
                        email = field
                    elif field != fields[-1]:
                        username = field
                password = fields[-1]
                if email:
                    ops.append((pymongo.UpdateOne({'_id': email}, {'$addToSet': {'passwords': password}}, upsert=True)))
                elif username:
                    ops.append((pymongo.UpdateOne({'_id': username}, {'$addToSet': {'passwords': password}}, upsert=True)))
                else:
                    pass # logic removed
            except UnicodeDecodeError:
                pass # logic removed

if __name__ == "__main__":
    parse(path/to/file.txt)

Я обнаружил, что расширений Pymongo C отсутствует в моей системе:

>>> import pymongo
>>> pymongo.has_c()
>>> False

Оттуда я сделал pip install --upgrade pymongo (к счастью для меня), и он принял значение True

Я также использовал _id вместо identifier для уникальных полей, что еще больше улучшило скорость.

Надеюсь, это поможет людям в будущем.Я буду обновлять с другими выводами, как я узнаю.

...