【问题标题】:Efficient data migration on a large django table大型 django 表上的高效数据迁移
【发布时间】:2011-09-10 10:39:14
【问题描述】:

我需要在一个大的(5m 行)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+ GB 的内存,然后才能通过表格很远,我放弃了。

似乎有什么东西在内存中保留这些对象。我怎样才能运行它,这样它就不会占用内存?

FWIW,我正在使用 python 2.6、django 1.2.1、South 0.7.2、mysql 5.1。

【问题讨论】:

    标签: python django performance django-models django-south


    【解决方案1】:

    确保 settings.DEBUG 设置为 FalseDEBUG=True 填充内存,尤其是数据库密集型操作,因为它将所有发送到 RDBMS 的查询存储在一个视图中。

    随着 Django 1.8 的发布,它应该没有必要了,因为现在存储了最多 9000 个硬编码查询,而不是之前的无限数量。

    【讨论】:

    • 帮我解决了。我认为 DEBUG 缓存了整个查询,因此如果您在数据迁移过程中插入 10 GB,它会使用(超过)10 GB 的内存。或者在我的情况下崩溃,因为我使用的是 32 位 PAE 内核。
    • 在较新版本的 Django 中是否仍需要此调整?
    • 在 Django 1.7 之前是必需的,但仍然建议:在 Django 1.7 或 1.8 中已经建立了存储在内存中的 9000 个查询的硬限制。
    • 从 Django 1.7 开始,这当然是必要的。我的迁移大约 500K 查询时内存不足,我已将 DEBUG 设置为 False,它运行良好。
    【解决方案2】:

    欢迎使用 Django 的 ORM。我认为这是一个固有的问题。

    我也遇到过大型数据库、dumpdata、loaddata 等问题。

    你有两个选择。

    1. 停止尝试使用 south 并编写自己的 ORM 迁移。您的设置中可以有多个数据库定义。创造“旧”和“新”。编写您自己的从旧数据库到新数据库的一次性迁移器。一旦测试成功,最后一次运行它,然后切换数据库定义并重新启动 Django。

    2. 抛弃南方和 ORM 并编写您自己的 SQL 迁移。使用原始 SQL 将数据从旧结构复制到新结构。单独调试。当它好的时候,最后一次运行它,然后切换你的设置并重新启动 Django。

    并不是说南方或 ORM 特别糟糕。但是,对于大型数据库中的批量处理,它们在内存中缓存过多。

    【讨论】:

      【解决方案3】:

      或者,如果您在原地创建一个实现基本结果集大小限制的原始查询会发生什么?

      啊啦: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()
      

      【讨论】:

        【解决方案4】:

        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 组 - 所以你需要解决这个问题为自己。

        【讨论】:

          【解决方案5】:

          如果您不需要对对象的完全访问权限,您始终可以在查询集上使用 onlyvaluesvalues_list 的组合。这应该有助于显着减少内存需求,但我不确定这是否足够。

          【讨论】:

            猜你喜欢
            • 2020-08-24
            • 2020-07-17
            • 2011-07-29
            • 2014-11-28
            • 2014-01-28
            • 2020-02-12
            • 2016-02-10
            • 2017-08-26
            • 1970-01-01
            相关资源
            最近更新 更多