高效迁移大型Django表数据
我需要在一个很大的 Django 表中添加一个新列,这个表有500万行数据。我已经有一个 south 的 schemamigration
,用来创建这个新列。现在我正在写一个 datamigration
脚本来填充这个新列。代码大概是这样的。(如果你不太了解 south 的迁移,可以忽略掉模型名称前面的 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
。但是当我运行这个脚本时,还是有东西在消耗大量内存。即使我把实际有用的那一行注释掉,脚本在处理表的时候,内存使用量还是会飙升到10GB以上,然后我就放弃了。
看起来有东西在内存中占用着这些对象。有什么办法可以让这个脚本不那么耗内存吗?
顺便说一下,我使用的是 Python 2.6、Django 1.2.1、South 0.7.2 和 MySQL 5.1。
5 个回答
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()
这样做会比较慢。我从我自己的一个独立迁移脚本中提取了这个,所以你的情况可能会有所不同。
fetchmany 是标准的方法(PEP249)。我没有完全实现你想要的功能,所以这个示例还有一些工作要做:我没有在循环中处理数据——也就是没有每次取500条数据直到完成——所以你需要自己解决这个问题。
或者,如果你在现场创建一个原始查询,并设置一个基本的结果集大小限制,会发生什么呢?
就像这样: 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()
确保 settings.DEBUG
设置为 False
。当 DEBUG=True
时,会占用很多内存,特别是在进行数据库操作时,因为它会把所有发送到数据库的查询都存储在一个视图中。
现在有了 Django 1.8 版本,这个问题应该不再那么严重,因为现在最多只会存储 9000 条查询,而之前是可以无限制存储的。