使用Django的ORM加速批量插入?

时间:2010-11-27 21:46:40

标签: django optimization orm bulkinsert

我计划使用django的ORM将从约750个文件(每个~250MB)中取出的十亿条记录上传到数据库。 目前每个文件需要大约20分钟来处理,我想知道是否有任何方法可以加速这个过程。

我采取了以下措施:

我还能做些什么来加快速度?以下是我的一些想法:

欢迎任何关于这些项目或任何其他想法的指示:)

7 个答案:

答案 0 :(得分:33)

答案 1 :(得分:16)

这不是特定于Django ORM,但最近我不得不将来自超过2000个文件的> 6000万行8列数据批量插入到sqlite3数据库中。我了解到以下三件事将插入时间从48小时减少到大约1小时:

  1. 增加数据库的缓存大小设置以使用更多RAM(默认值总是非常高 小,我用3GB);在sqlite中,这是由PRAGMA cache_size = n_of_pages完成的;

  2. 在RAM而不是磁盘中进行日志记录(这确实会造成轻微的影响 系统失败的问题,但我认为可以忽略不计 鉴于你已经在磁盘上有源数据);在sqlite中,这是由PRAGMA journal_mode = MEMORY

  3. 完成的
  4. 最后也许是最重要的一个:不要建立索引 插入。这也意味着不要声明可能导致DB构建索引的UNIQUE或其他约束。完成插入后才构建索引。

  5. 如前所述,您还应该使用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上运行了一些测试,得到了以下时间:

  • 单独插入3000行并使用Django ORM从填充的对象获取ID: 3200ms
  • 使用Pandas DataFrame.to_sql()插入3000行,并且不会获取ID: 774ms
  • 使用Django管理员.bulk_create(Model(**df.to_records()))插入3000行,但不会获取ID: 574ms
  • 使用to_csvStringIO缓冲区和COPYcur.copy_from())插入3000行,并且无法获取ID: 118ms < / LI>
  • 使用to_csvCOPY插入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)