Pymongo занимает более 24 часов, чтобы перебрать записи 200K - PullRequest
3 голосов
/ 11 февраля 2012

У меня есть две коллекции в БД page и pagearchive Я пытаюсь очистить.Я заметил, что в pagearchive создаются новые документы вместо добавления значений во встроенные документы, как предполагалось.По сути, этот скрипт выполняет все документы в page, а затем находит все копии этого документа в pagearchive и перемещает нужные мне данные в один документ и удаляет дополнительные элементы.

Проблемаесли в pagearchive есть только 200 тыс. документов, и, исходя из переменной подсчета, которую я печатаю внизу, для перебора 1000 записей требуется от 30 минут до 60+ минут.Это очень медленно.Наибольшее число дублирующих документов, которые я видел, составляет 88. Но по большей части, когда я запрашиваю в pageArchive на uu, я вижу 1-2 дубликата документов.бит машина с 16 ГБ оперативной памяти.Ключ uu, который перебирает коллекцию pageArchive, является строкой.Я убедился, что в этом поле есть индекс db.pagearchive.ensureIndex({uu:1}) Я также сделал mongod --repair для хорошей меры.

Мне кажется, проблема в моем неаккуратном коде Python (не очень хорошо) или, возможното, чего мне не хватает, что необходимо для mongodb.Почему это происходит так медленно или что я могу сделать, чтобы значительно ускорить его?

Я подумал, может быть, потому что поле uu представляет собой строку, которая вызывает узкое место, но это уникальное свойство в документе (или будет после очистки этой коллекции).Кроме того, когда я останавливаю и перезапускаю процесс, он ускоряет до 1000 записей в секунду.Пока он не начнет снова находить дубликаты в коллекции, он снова будет работать медленно (удаляя около 100 записей каждые 10-20 минут)

from pymongo import Connection
import datetime


def match_dates(old, new):
    if old['coll_at'].month == new['coll_at'].month and old['coll_at'].day == new['coll_at'].day and old['coll_at'].year == new['coll_at'].year:
        return False

    return new

connection = Connection('dashboard.dev')


db = connection['mydb']

pageArchive = db['pagearchive']
pages = db['page']

count = 0
for page in pages.find(timeout=False):

    archive_keep = None
    ids_to_delete = []
    for archive in pageArchive.find({"uu" : page['uu']}):

        if archive_keep == None:
            #this is the first record we found, so we will store data from duplicate records with this one; delete the rest
            archive_keep = archive
        else:
            for attr in archive_keep.keys():
                #make sure we are dealing with an embedded document field
                if isinstance(archive_keep[attr], basestring) or attr == 'updated_at':
                    continue
                else:
                    try:
                        if len(archive_keep[attr]) == 0:
                            continue
                    except TypeError:
                        continue
                    try:
                        #We've got our first embedded doc from a property to compare against
                        for obj in archive_keep[attr]:
                            if archive['_id'] not in ids_to_delete:
                                ids_to_delete.append(archive['_id'])
                            #loop through secondary archive doc (comparing against the archive keep)
                            for attr_old in archive.keys():
                                #make sure we are dealing with an embedded document field
                                if isinstance(archive[attr_old], basestring) or attr_old == 'updated_at':
                                    continue
                                else:
                                    try:
                                        #now we know we're dealing with a list, make sure it has data
                                        if len(archive[attr_old]) == 0:
                                            continue
                                    except TypeError:
                                        continue
                                    if attr == attr_old:
                                        #document prop. match; loop through embedded document array and make sure data wasn't collected on the same day
                                        for obj2 in archive[attr_old]:
                                            new_obj = match_dates(obj, obj2)
                                            if new_obj != False:
                                                archive_keep[attr].append(new_obj)
                    except TypeError, te:
                        'not iterable'
        pageArchive.update({
                            '_id':archive_keep['_id']}, 
                           {"$set": archive_keep}, 
                           upsert=False)
        for mongoId in ids_to_delete:
            pageArchive.remove({'_id':mongoId})
        count += 1
        if count % 100 == 0:
            print str(datetime.datetime.now()) + ' ### ' + str(count) 

1 Ответ

2 голосов
/ 11 февраля 2012

Я бы сделал следующие изменения в коде:

  • в match_dates вернул None вместо False и сделал бы if new_obj is not None:, он проверит ссылку, не вызывая объект __ne__ или __nonzero__.

  • for page in pages.find(timeout=False): Если используется только ключ uu и страницы большие, параметр fields=['uu'] для find должен ускорять запросы.

  • archive_keep == None до archive_keep is None

  • archive_keep[attr] вызывается 4 раза.Будет немного быстрее сохранить keep_obj = archive_keep[attr], а затем использовать keep_obj.

  • изменить ids_to_delete = [] на ids_to_delete = set().Тогда if archive['_id'] not in ids_to_delete: будет O (1)

...