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

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

我采取了以下措施:

  • 每使用@ transaction.commit_manually并提交一次,每5000条logging
  • 设置DEBUG = False,这样django 就不会在内存中累积所有的sql命令
  • 循环遍历单个文件中的logging完全包含在单个函数中(最小化堆栈更改)
  • 从敲击查询数据库(使用已经在db中的对象的本地散列而不是使用get_or_create )
  • 在save()中设置force_insert = True,希望能够保存django的一些逻辑
  • 显式设置id希望能够保存django的一些逻辑
  • 通用代码最小化和优化

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

  • 使用某种Python编译器或更快的版本(Psyco?)
  • 重写ORM并直接使用SQL
  • 使用一些可能更好的第三方代码( 1,2 )
  • 请求django社区创build一个bulk_insert函数

任何有关这些项目或任何其他想法的指针将受到欢迎:)

这不是特定于Django的ORM,但最近我不得不从2000多个文件中将大于6千万行的8列数据插入sqlite3数据库。 而且我知道以下三件事将插入时间从48小时减less到〜1小时:

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

  2. 在RAM中而不是在磁盘上进行日志logging(如果系统出现故障,这会造成轻微的问题,但考虑到您已经在磁盘上有源数据,我认为这是微不足道的)。 在sqlite中这是由PRAGMA journal_mode = MEMORY完成的

  3. 最后也许是最重要的一点:插入时不要build立索引。 这也意味着不声明可能导致DBbuild立索引的UNIQUE或其他约束。 只有在完成插入后才能build立索引。

正如前面提到的,你也应该使用cursor.executemany()(或者只是快捷方式conn.executemany())。 要使用它,请执行:

 cursor.executemany('INSERT INTO mytable (field1, field2, field3) VALUES (?, ?, ?)', iterable_data) 

iterable_data可以是一个列表或类似的东西,甚至是一个开放的文件阅读器。

放到DB-API并使用cursor.executemany() 。 详情请参阅PEP 249 。

http://djangosnippets.org/snippets/446/还有一个批量插入片段。;

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

它也似乎是大量logging,这总是一个加号。

另外,如果你想快速简单的事情,你可以试试这个: http : //djangosnippets.org/snippets/2362/ 。 这是我在一个项目上使用的一个简单的经理。

另一个片段并不是那么简单,而是真正专注于关系的批量插入。 这只是一个普通的批量插入,只是使用相同的INSERT查询。

发展django得到bulk_create: https ://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create

我在Django 1.10 / Postgresql 9.4 / Pandas 0.19.0上运行了一些testing,得到如下的时间:

  • 单独插入3000行,并使用Django ORM: 3200ms从填充对象获取ID
  • 用Pandas DataFrame.to_sql()插入3000行,不会得到ID: 774ms
  • 用Django manager .bulk_create(Model(**df.to_records()))插入3000行,不会得到ID: 574ms
  • to_csv插入3000行到StringIO缓冲区并COPYcur.copy_from() ),不要得到ID: 118ms
  • to_csvCOPY插入3000行,并通过简单的SELECT WHERE ID > [max ID before insert] (可能不是线程安全,除非COPY在表上阻止同时插入的locking)获得ID: 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() 

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

我通过分配一个导入的批次ID和主键sorting来得到我插入的主键,尽pipe我不是100%确定的主键将始终按照COPY命令序列化的顺序进行分配 – 不pipe怎样, 。