使用Django的ORM加速批量插入?
使用Django的ORM加速批量插入?
我计划使用django的ORM将从大约750个文件(每个约250MB)中获取的10亿条记录上传到数据库中。
目前每个文件需要大约20分钟进行处理,我想知道是否有任何方法可以加快这个过程。
我已经采取了以下措施:
- 使用@transaction.commit_manually,每5000条记录提交一次
- 将DEBUG设置为False,以使django 不会在内存中累积所有的SQL命令
- 在一个函数中完全包含运行单个文件中的记录的循环(减少栈的变化)
- 避免对数据库进行查询(使用本地对象的哈希值而不是使用get_or_create)
- 在save()中设置force_insert=True,希望能为django节省一些逻辑
- 明确设置id,希望能为django节省一些逻辑
- 代码的最小化和优化
还有什么其他方法可以加快速度吗?以下是我的一些想法:
- 使用一种更快的Python编译器或版本(Psyco?)
- 覆盖ORM,直接使用SQL
- 使用一些可能更好的第三方代码(1,2)
- 请求django社区创建一个批量插入函数
关于这些事项或任何其他想法的任何指导都将受到欢迎 🙂
原因:需要将超过2000个文件中的8列数据批量插入SQLite3数据库,但插入时间超过48小时。通过增加数据库的缓存大小、将日志写入RAM而不是磁盘以及在插入时不建立索引,可以将插入时间缩短到大约1小时。
解决方法:
1. 增加数据库的缓存大小,使用更多的RAM。在SQLite中,可以通过设置PRAGMA cache_size = n_of_pages来实现。默认的缓存大小通常很小,可以尝试使用3GB的缓存大小。
2. 将日志写入RAM而不是磁盘。虽然这样做可能在系统故障时会造成轻微问题,但考虑到源数据已经存储在磁盘上,这个问题可以忽略不计。在SQLite中,可以通过设置PRAGMA journal_mode = MEMORY来实现。
3. 在插入时不建立索引。这也意味着不声明UNIQUE或其他可能导致数据库建立索引的约束。只有在插入完成后再建立索引。
另外,建议使用cursor.executemany()方法来执行批量插入操作。可以使用以下代码示例:
cursor.executemany('INSERT INTO mytable (field1, field2, field3) VALUES (?, ?, ?)', iterable_data)
其中,iterable_data可以是一个列表或类似的数据结构,甚至可以是一个打开的文件读取器。
以上解决方法也适用于使用peewee ORM的情况。如果遇到批量插入的性能问题,可以参考上述方法进行优化。
参考链接中还提到了一个关于如何在Django中暂时禁用索引的问题,可以参考该问题的解答进一步了解。
通过增加缓存大小、将日志写入RAM以及在插入时不建立索引,可以加速使用Django ORM进行批量插入的操作。
在Django 1.4中,提供了QuerySet对象上的bulk_create()方法,用于批量插入数据。但是,如果需要自动生成主键,就不能使用该方法。至少在某些数据库(如MySQL、Postgres)上,可以通过使用原始查询来实现自动生成主键。
关于自动生成主键,至少从1.11版本开始,PostgreSQL可以实现该功能。如果考虑使用bulk_create来提高性能,请注意参考代码.djangoproject.com/ticket/28231的附加说明。
因此,如果需要批量插入数据并且不需要自动生成主键,可以使用QuerySet对象的bulk_create()方法。如果需要自动生成主键,可以使用原始查询来实现。
解决方法:
- 如果不需要自动生成主键,可以使用QuerySet对象的bulk_create()方法来进行批量插入。
- 如果需要自动生成主键,可以使用原始查询来实现。
参考链接:
- Django 1.4提供的bulk_create()方法:https://docs.djangoproject.com/en/stable/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create
- Django 1.4发布说明:https://docs.djangoproject.com/en/stable/releases/1.4/
- 相关的问题和讨论:https://code.djangoproject.com/ticket/7596
- 自动生成主键的支持:https://code.djangoproject.com/ticket/28231