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

Jon*_*han 44 django optimization orm bulkinsert

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

我采取了以下措施:

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

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

Gar*_*ary 34

Django 1.4 bulk_create()在QuerySet对象上提供了一个方法,参见:

  • 除非你需要自动主键.如果您使用原始查询,至少可以使用某些数据库(MySQL,Postgres). (2认同)

Yan*_*Cao 17

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

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

  2. 在RAM而不是磁盘中进行日志记录(如果系统出现故障,这确实会引起轻微问题,但考虑到磁盘上已有源数据,我认为可以忽略不计); 在sqlite中,这是由PRAGMA journal_mode = MEMORY完成的

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

如前所述,您还应该使用cursor.executemany()(或只是快捷方式conn.executemany()).要使用它,请执行:

cursor.executemany('INSERT INTO mytable (field1, field2, field3) VALUES (?, ?, ?)', iterable_data)
Run Code Online (Sandbox Code Playgroud)

iterable_data可以是列表或类似的东西,甚至是打开的文件阅读器.


Ign*_*ams 12

删除到DB-API并使用cursor.executemany().有关详细信息,请参阅PEP 249.


Chr*_*ris 7

我在Django 1.10/Postgresql 9.4/Pandas 0.19.0上运行了一些测试,得到了以下时间:

  • 单独插入3000行,并使用Django ORM从3200ms获取填充对象的ID
  • 使用Pandas插入3000行,DataFrame.to_sql()但不获取ID:774ms
  • 使用Django管理器插入3000行,df.to_sql()但不获取ID:574ms
  • 插入3000行与.bulk_create(Model(**df.to_records()))to_csv缓冲和StringIO(COPY),并没有得到编号:118ms
  • 使用cur.copy_from()和插入3000行并to_csv通过简单获取ID COPY(可能不是线程安全,除非SELECT WHERE ID > [max ID before insert]在表上保持锁定以防止同时插入?):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()
Run Code Online (Sandbox Code Playgroud)

性能统计数据全部在已经包含在OS X(i7 SSD 16GB)上运行的3,000行的表上获得,平均使用10次运行COPY.

我通过分配导入批处理ID并按主键排序来获取插入的主键,尽管我不是100%确定主键始终按照命令的行序列分配timeit- 无论如何都会欣赏意见.


Sea*_*aux 5

http://djangosnippets.org/snippets/446/上还有一个批量插入代码段.

这给出了一个插入命令多个值对(INSERT INTO x(val1,val2)VALUES(1,2),(3,4)--etc等).这应该会大大提高性能.

它似乎也有大量记录,这总是一个加号.