【问题标题】:Best practice of bulk_create for massive recordsbulk_create 海量记录的最佳实践
【发布时间】:2015-12-24 16:16:54
【问题描述】:

我使用bulk_create 将 1 条 mio 记录插入到新表中。这需要 80 秒。 Django 只使用一个 CPU 内核(大约 25% CPU,但没有一个内核达到 100%)我相信有改进的潜力。

这里是代码

class Stock(models.Model):
    code = models.CharField(max_length=6, unique=True)
    name = models.CharField(max_length=8)

class Watchers(models.Model):
    date = models.DateField(db_index=True)
    stock = models.ForeignKey(Stock, unique_for_date="date")
    num = models.PositiveIntegerField(default=0)

batch = []
for input in inputs:
    watcher = Watcher(date=input['date'], stock=get_stock(), num=input['num'])
    batch.append(watcher)
Watcher.objects.bulk_create(batch)

我已经尝试了几件事:

  1. 使用正确的batch_size。对我来说最好的值大约是 4000。这需要 80-90 秒。
  2. 使用ThreadPool()。它要慢得多,大约 120-140 秒
  3. 删除DateField 上的索引。比 1 慢一点。

我正在使用 MySQL 5.6 社区版。存储引擎是 MyISAM。这是配置。

[mysqld]
server-id   = 1
default-storage-engine = MyISAM
port        = 3306
socket      = /tmp/mysql.sock
datadir     = "{MYSQLDATAPATH}"
skip-external-locking
explicit_defaults_for_timestamp = TRUE

# MyISAM #
key-buffer-size = 32M
max_allowed_packet = 16M

# CACHES AND LIMITS #
tmp-table-size                 = 32M
max-heap-table-size            = 32M
query-cache-type               = 0
query-cache-size               = 0
max-connections                = 500
thread-cache-size              = 50
open-files-limit               = 65535
table-definition-cache         = 1024
table-open-cache               = 2048

# LOGGING
log-bin       = mysql-bin
binlog_format = mixed

当我导入另一个表(结构相似,索引相同,但有 9 列)时,需要 15 分钟。时间增加不是线性的。

bulk_create 有什么问题吗?


更新 1

虽然我已经接受了答案,但我想我应该明白其中的奥秘。于是又做了一些测试,发现Django的模型创建速度变慢的根本原因。当我有 800000 条记录时,调用 800000 次创建模型将非常耗时。

ORM 框架做了很多我们看不到的内部工作,例如完整性检查。在我的例子中,大量的记录会被导入到一个空的数据库表中,所以检查是没有必要的。

现在我使用cursor.executemany(),它将800000条4列记录的插入时间从54秒缩短到16秒。并将800000条13列记录的插入时间从13分钟缩短到46秒。

根据我的实验,您会为每 3000-5000 条记录调用 executemany。我试过一次调用 80 万条记录,这非常慢。

【问题讨论】:

  • 批量创建没有问题,但它并不是真正设计为一次将数百万条记录加载到数据库中。对于一天结束时的那种任务,没有什么比使用 LOAD DATA IN FILE 更好
  • @e4c5 我相信LOAD DATA IN LOCAL FILE 会更快。您要发布答案以便我接受吗?
  • 非常感谢。抱歉耽搁了。实际上,我已经完全忘记了 LOCAL 选项,所以回答您的问题就像参加进修课程。
  • key_buffer_size 应该是 可用 RAM 的 10-20%。
  • @RickJames 感谢您的提示。但似乎我的案例并没有产生太多的 mysql 数据(大约 50MB)。真正的瓶颈是 django 的 ORM。它为查询数据提供了便利,但构造海量对象,效率不高。

标签: mysql django performance orm


【解决方案1】:

虽然 bulk_create 对于在处理 HTML 表单时保存少量记录很有用,但它并不适合保存数千条记录。正如您已经发现的那样,它很慢,因为它需要大量内存并向数据库发送非常大的查询。 Fortunatley LOAD DATA IN FILE 前来救援。

LOAD DATA INFILE 语句将文本文件中的行读取到 表以非常高的速度。 LOAD DATA INFILE 是 选择...进入文件。

我们可以生成一个类似于使用csv writer 生成的文件,以下示例来自文档。

import csv
    with open('some.csv', 'wb') as f:
    writer = csv.writer(f)
    writer.writerows(someiterable)

最后,正如您已经发现的那样,为了方便起见,有时可以使用 LOCAL 选项 LOAD DATA ..。

LOCAL 仅在您的服务器和客户端都已 配置为允许它

使用此选项时,不需要手动将文件传输到服务器。您可以在客户端生成 CSV 文件,本地选项将导致 mysql 客户端自动将文件传输到服务器。

如果未指定 LOCAL,则文件必须位于服务器主机上 并由服务器直接读取。

【讨论】:

  • 我终于有了一个很好的解决方案。请参阅问题的“更新 1”部分
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 2012-01-10
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 2011-04-10
相关资源
最近更新 更多