Массовое обновление Python Peewee MySQL

Я использую Python 2.7, Peewee и MySQL. Моя программа читает из CSV-файла и обновляет поле, если в CSV-файле существует номер заказа. Может быть 2000-3000 обновлений, и я использую наивный подход, чтобы обновлять записи по одному, что очень медленно. Я перешел от использования обновления Peewee к необработанным запросам, что немного быстрее. Тем не менее, это все еще очень медленно. Мне было интересно, как обновить записи за меньшее количество транзакций без использования цикла.

def mark_as_uploaded_to_zoho(self, which_file):
    print "->Started marking the order as uploaded to zoho."
    with open(which_file, 'rb') as file:
        reader = csv.reader(file, encoding='utf-8')
        next(reader, None) ## skipping the header

        for r in reader:
            order_no = r[0]
            query = '''UPDATE sales SET UploadedToZoho=1 WHERE OrderNumber="%s" and UploadedToZoho=0''' %order_no
            SalesOrderLine.raw(query).execute()

    print "->Marked as uploaded to zoho."

person Muhit Anik    schedule 01.08.2017    source источник


Ответы (1)


Вы можете использовать insert_many, чтобы ограничить количество транзакций и получить большой прирост скорости. Для этого требуется итератор, который возвращает объекты словаря, где поля модели соответствуют ключам словаря.

В зависимости от того, сколько записей вы пытаетесь вставить, вы можете либо сделать их все сразу, либо разбить на более мелкие фрагменты. В прошлом я вставлял более 10 000 записей за раз, но это может быть очень медленно в зависимости от спецификаций сервера БД и клиента, поэтому я покажу оба способа.

with open(which_file, 'rb') as file:
    reader = csv.DictReader(file)
    SalesOrderLine.insert_many(reader)

OR

# Calls a function with chunks of an iterable as list.
# Not memory efficient at all.
def chunkify(func, iterable, chunk_size):
    chunk = []
    for o in iterable:
        chunk.append(o)
        if len(chunk) > chunk_size:
            func(chunk)
            chunk = []

with open(which_file, 'rb') as file:
    reader = csv.DictReader(file)
    chunkify(SalesOrderLine.insert_many, reader, 1000)

Чтобы узнать о более эффективных способах «объединения» итераторов, ознакомьтесь с этим вопрос.

Дополнительную скорость можно получить, просто используя with db.atomic, как описано здесь.

person wyattis    schedule 17.08.2017
comment
Заданный вопрос касается массового обновления, а не вставки. - person QA Collective; 05.10.2018