我计划使用django的ORM将从大约750个文件(每个文件大约250MB)中提取的10亿条记录上传到数据库。目前每个文件需要大约20分钟的处理时间,我想知道是否有任何方法可以加速这个过程。
我采取了以下措施:
- Use @transaction.commit_manually并每5000条记录提交一次
- 设置DEBUG=False,这样django won't accumulate all the sql commands in memory
- 在单个文件中的记录上运行的循环完全包含在单个函数中(最小化堆栈更改)
- 避免查询数据库(使用数据库instead of using get_or_create中已有对象的本地散列)
- 在保存()中设置force_insert=True,希望它能为django节省一些逻辑
- 显式地设置id,希望它能为Django节省一些逻辑
- 通用代码最小化和优化
我还能做些什么来加快速度呢?以下是我的一些想法:
- 使用某种Python编译器或更快的版本(Psyco?)
- Override the ORM and use SQL directly
- 使用一些可能更好的第三方代码(1,2)
- Beg the django community创建bulk_insert函数
任何关于这些项目的指针或任何其他想法都是受欢迎的:)
7条答案
按热度按时间jum4pzuy1#
Django 1.4在QuerySet对象上提供了一个
bulk_create()
方法,参见:fdbelqdn2#
这并不是Django ORM特有的,但最近我不得不从超过2000个文件中批量插入超过6000万行8列数据到sqlite3数据库中。我了解到以下三件事将插入时间从超过48小时减少到1小时:
1.增加数据库该高速缓存大小设置以使用更多的RAM(默认值总是很小,我使用了3GB);在sqlite中,这是通过PRAGMA缓存大小= n个页面完成;
1.在RAM而不是磁盘中进行日志记录(如果系统出现故障,这确实会导致轻微的问题,但我认为考虑到磁盘上已经有源数据,这是可以忽略不计的);在sqlite中,这是通过PRAGMA journal_mode = MEMORY完成
1.最后一个,也许也是最重要一个:插入时不要建立索引。这也意味着不要声明UNIQUE或其他可能导致DB建立索引的约束。只有在插入完成后才建立索引。
正如前面有人提到的,你还应该使用cursor. executemmany()(或者只是快捷方式conn. executemmany())。要使用它,请执行以下操作:
iterable_data可以是一个列表或类似的东西,甚至可以是一个打开的文件读取器。
beq87vna3#
使用DB-API,使用
cursor.executemany()
。详情请参阅PEP 249。wvyml7n54#
我在Django 1.10 / Postgresql 9.4 / Pandas 0.19.0上运行了一些测试,得到了以下时间:
DataFrame.to_sql()
插入3000行,但不获取ID:774ms.bulk_create(Model(**df.to_records()))
插入3000行,并且不获取ID:574msto_csv
到StringIO
缓冲区和COPY
(cur.copy_from()
),但不获取ID:118msto_csv
和COPY
插入3000行,并通过简单的SELECT WHERE ID > [max ID before insert]
获取ID(可能不是线程安全的,除非COPY
在表上持有锁,防止同时插入?):201ms性能统计数据都是在一个已经包含3,000行的表上获得的,该表在OS X(i7 SSD 16 GB)上运行,平均使用
timeit
运行10次。我通过分配一个导入批处理ID并按主键排序来获取插入的主键,尽管我不能100%确定主键总是按照
COPY
命令序列化行的顺序分配-无论哪种方式都很感激。2020年更新:
我在Pandas〉= 0.24中测试了新的
to_sql(method="multi")
功能,它将所有插入放入单个多行插入语句中。令人惊讶的是,性能比单行版本 * 差 *,无论是Pandas版本0.23,0.24或1.1。Pandas的单行插入也比直接向数据库发出的多行插入语句快。这次我在一个更大的数据库中使用更复杂的数据,但是to_csv
和cursor.copy_from
仍然比最快的替代方案快38%左右,这是一个单行df.to_sql
,bulk_import
偶尔可以比较,但通常还是慢一些(最多两倍的时间,Django 2.2)。tct7dpnv5#
在http://djangosnippets.org/snippets/446/处还有一个批量插入代码段。
这就给了一个插入命令多个值对(INSERT INTO x(val 1,val 2)VALUES(1,2),(3,4)--等等)。这将极大地提高性能。
它似乎也有大量的记录,这总是一个加号。
qc6wkl3g6#
如果你想要快速和简单的东西,你可以试试这个:http://djangosnippets.org/snippets/2362/。这是我在一个项目中使用的一个简单的管理器。
另一个代码段就不那么简单了,它真正关注的是关系的批量插入。这只是一个普通的批量插入,只是使用了相同的INSERT查询。
13z8s7eq7#
开发Django得到了bulk_create:https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create