我计划使用django的ORM将从约750个文件(每个~250MB)中取出的十亿条记录上传到数据库。 目前每个文件需要大约20分钟来处理,我想知道是否有任何方法可以加速这个过程。
我采取了以下措施:
我还能做些什么来加快速度?以下是我的一些想法:
欢迎任何关于这些项目或任何其他想法的指示:)
答案 0 :(得分:33)
Django 1.4在QuerySet对象上提供了bulk_create()
方法,参见:
答案 1 :(得分:16)
这不是特定于Django ORM,但最近我不得不将来自超过2000个文件的> 6000万行8列数据批量插入到sqlite3数据库中。我了解到以下三件事将插入时间从48小时减少到大约1小时:
增加数据库的缓存大小设置以使用更多RAM(默认值总是非常高 小,我用3GB);在sqlite中,这是由PRAGMA cache_size = n_of_pages完成的;
在RAM而不是磁盘中进行日志记录(这确实会造成轻微的影响 系统失败的问题,但我认为可以忽略不计 鉴于你已经在磁盘上有源数据);在sqlite中,这是由PRAGMA journal_mode = MEMORY
最后也许是最重要的一个:不要建立索引 插入。这也意味着不要声明可能导致DB构建索引的UNIQUE或其他约束。完成插入后才构建索引。
如前所述,您还应该使用cursor.executemany()(或只是快捷方式conn.executemany())。要使用它,请执行以下操作:
cursor.executemany('INSERT INTO mytable (field1, field2, field3) VALUES (?, ?, ?)', iterable_data)
iterable_data可以是一个列表或类似的东西,甚至是一个打开的文件阅读器。
答案 2 :(得分:12)
Drop to DB-API并使用cursor.executemany()
。有关详细信息,请参阅PEP 249。
答案 3 :(得分:6)
我在Django 1.10 / Postgresql 9.4 / Pandas 0.19.0上运行了一些测试,得到了以下时间:
DataFrame.to_sql()
插入3000行,并且不会获取ID: 774ms
df.to_sql()
更快,可选择combining multiple inserts into a single statement - 我还没有测试过它.bulk_create(Model(**df.to_records()))
插入3000行,但不会获取ID: 574ms to_csv
向StringIO
缓冲区和COPY
(cur.copy_from()
)插入3000行,并且无法获取ID: 118ms < / LI>
to_csv
和COPY
插入3000行,并通过简单的SELECT WHERE ID > [max ID before insert]
获取ID(可能不是线程安全的,除非COPY
持有对表的锁定以防止同时插入?):的 201ms 强> def bulk_to_sql(df, columns, model_cls):
""" Inserting 3000 takes 774ms avg """
engine = ExcelImportProcessor._get_sqlalchemy_engine()
df[columns].to_sql(model_cls._meta.db_table, con=engine, if_exists='append', index=False)
def bulk_via_csv(df, columns, model_cls):
""" Inserting 3000 takes 118ms avg """
engine = ExcelImportProcessor._get_sqlalchemy_engine()
connection = engine.raw_connection()
cursor = connection.cursor()
output = StringIO()
df[columns].to_csv(output, sep='\t', header=False, index=False)
output.seek(0)
contents = output.getvalue()
cur = connection.cursor()
cur.copy_from(output, model_cls._meta.db_table, null="", columns=columns)
connection.commit()
cur.close()
性能统计数据全部在已经包含在OS X(i7 SSD 16GB)上运行的3,000行的表上获得,使用timeit
平均运行10次。
我通过分配导入批处理ID并按主键排序来获取插入的主键,尽管我不是100%确定主键始终按行序列化为{{1命令 - 无论如何都会欣赏意见。
答案 4 :(得分:5)
http://djangosnippets.org/snippets/446/还有一个批量插入代码段。
这为一个插入命令提供了多个值对(INSERT INTO x(val1,val2)VALUES(1,2),(3,4) - etc等)。这应该会大大提高性能。
它似乎也有大量记录,这总是一个加号。
答案 5 :(得分:3)
此外,如果您想要快速简单的事情,可以试试这个:http://djangosnippets.org/snippets/2362/。这是我在项目中使用的一个简单的经理。
其他代码段并不那么简单,而且专注于关系的批量插入。这只是一个普通的批量插入,只使用相同的INSERT查询。
答案 6 :(得分:3)