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

quhf5bfb  于 2023-03-31  发布在  Go
关注(0)|答案(7)|浏览(184)

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

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

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

jum4pzuy

jum4pzuy1#

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

fdbelqdn

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())。要使用它,请执行以下操作:

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

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

beq87vna

beq87vna3#

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

wvyml7n5

wvyml7n54#

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

  • 分别插入3000行,并使用Django ORM从填充的对象中获取id:3200毫秒
  • 使用Pandas DataFrame.to_sql()插入3000行,但不获取ID:774ms
  • 使用Django管理器.bulk_create(Model(**df.to_records()))插入3000行,并且不获取ID:574ms
  • 插入3000行to_csvStringIO缓冲区和COPYcur.copy_from()),但不获取ID:118ms
  • 使用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()

性能统计数据都是在一个已经包含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_csvcursor.copy_from仍然比最快的替代方案快38%左右,这是一个单行df.to_sqlbulk_import偶尔可以比较,但通常还是慢一些(最多两倍的时间,Django 2.2)。

tct7dpnv

tct7dpnv5#

http://djangosnippets.org/snippets/446/处还有一个批量插入代码段。
这就给了一个插入命令多个值对(INSERT INTO x(val 1,val 2)VALUES(1,2),(3,4)--等等)。这将极大地提高性能。
它似乎也有大量的记录,这总是一个加号。

qc6wkl3g

qc6wkl3g6#

如果你想要快速和简单的东西,你可以试试这个:http://djangosnippets.org/snippets/2362/。这是我在一个项目中使用的一个简单的管理器。
另一个代码段就不那么简单了,它真正关注的是关系的批量插入。这只是一个普通的批量插入,只是使用了相同的INSERT查询。

13z8s7eq

13z8s7eq7#

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

相关问题