Эффективная миграция данных на большой таблице django - PullRequest
7 голосов
/ 08 июня 2011

Мне нужно добавить новый столбец в большую (5м строку) таблицу django. У меня есть юг schemamigration, который создает новый столбец. Сейчас я пишу datamigration скрипт для заполнения нового столбца. Похоже на это. (Если вы не знакомы с миграцией на юг, просто игнорируйте orm. с префиксом имени модели.)

print "Migrating %s articles." % orm.Article.objects.count()
cnt = 0
for article in orm.Article.objects.iterator():            
    if cnt % 500 == 0:
        print "    %s done so far" % cnt
    # article.newfield = calculate_newfield(article)
    article.save()
    cnt += 1

Я переключился с objects.all на objects.iterator, чтобы уменьшить требования к памяти. Но когда я запускаю этот скрипт, что-то все еще пережевывает огромную память. Даже с действительно полезной строкой, закомментированной, как указано выше, сценарий по-прежнему переходит к использованию более 10 ГБ оперативной памяти, прежде чем пройти очень далеко через таблицу, и я разочаровываюсь в этом.

Похоже, что-то держит эти объекты в памяти. Как я могу запустить это, чтобы это не боров памяти?

FWIW, я использую python 2.6, django 1.2.1, south 0.7.2, mysql 5.1.

Ответы [ 5 ]

7 голосов
/ 12 августа 2011

Убедитесь, что settings.DEBUG установлено на False.DEBUG=True заполняет память, особенно при выполнении интенсивных операций с базами данных, поскольку в нем хранятся все запросы, отправленные в СУБД в представлении.

С выходом Django 1.8 в этом нет необходимости, поскольку в настоящее время хранится до 9000 жестко закодированных макс.вместо бесконечного числа до.

2 голосов
/ 08 июня 2011

Или, что произойдет, если вы создадите необработанный запрос на месте, который реализует элементарный предел размера набора результатов?

а-ля: https://docs.djangoproject.com/en/1.3/topics/db/sql/#index-lookups

while min < rowcount:
  min += 500
  max = min + 500
  articles = Article.objects.raw('SELECT * from article where id > %s and id < %s' % (min, max))
  for old_article in articles:
    # create the new article
    article.save()
2 голосов
/ 08 июня 2011

orm.Article.objects.iterator()

Это запускает весь запрос и сохраняет результат в памяти? Или извлекать строки из базы данных по одной за раз?

Полагаю, все сразу. Посмотрите, можете ли вы заменить этот цикл курсором базы данных, который извлекает данные инкрементно:

Например: http://docs.python.org/library/sqlite3.html#sqlite3.Cursor.fetchmany

db = blah.connect("host='%s' dbname='%s' user='%s' password='%s'" % ...
new, old = db.cursor(), db.cursor()
old.execute("""
    SELECT  *
    FROM    whatever
""")
for row in old.fetchmany(size=500):
    (col1, col2, col3...) = row
    new = db.cursor()
    new.execute("""
        INSERT INTO yourtable (
            col1, col2, col3...)
        VALUES (
            %s, %s, %s, %s, %s)
        """,(col1, col2, col3,...))
new.close()
old.close()

Это будет медленно. Я взял это из моего автономного сценария миграции, так что ymmv.

fetchmany является стандартным (PEP249). Я не сделал именно то, что вы ищете, так что из этого примера еще предстоит проделать небольшую работу: я не зацикливался на цикле - чтобы получить наборы по 500 до конца - так что вам нужно решить это для себя.

2 голосов
/ 08 июня 2011

Добро пожаловать в ORM Джанго. Я думаю, что это врожденная проблема.

У меня также были проблемы с большими базами данных, dumpdata, loaddata и т. П.

У вас есть два варианта.

  1. Перестаньте пытаться использовать юг и напишите собственную миграцию ORM. Вы можете иметь несколько определений базы данных в ваших настройках. Создайте «старый» и «новый». Напишите свой собственный одноразовый переносчик из старой базы данных в новую базу данных. После того, как это проверено и работает, запустите его в последний раз, затем переключите определения базы данных и перезапустите Django.

  2. Перейдите на юг и ORM и напишите собственную миграцию SQL. Используйте сырой SQL для копирования данных из старой структуры в новую структуру. Отлаживать отдельно. Когда все будет хорошо, запустите его в последний раз, затем измените настройки и перезапустите Django.

Дело не в том, что юг или ОРМ особенно плохи. Но для массовой обработки в больших базах данных они слишком много кешируют в памяти.

1 голос
/ 08 июня 2011

Если вам не нужен полный доступ к объектам, вы всегда можете использовать комбинацию only и values или values_list в вашем наборе запросов.Это должно значительно снизить требования к памяти, но я не уверен, будет ли этого достаточно.

...