7

我正在使用Django 适配器上传一个简单的 CSV。当我导入 100 或 200 个联系人时,它似乎工作得很好。但是当我尝试上传一个包含 5000 个联系人的 165kb 文件时,它永远不会完成。我让它继续尝试,当我在 1 小时后回来时,它仍在尝试。

这有什么问题?使用Django 适配器导入一个 165kb 的文件不可能花费一个多小时。代码有问题吗?

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

CSV模型

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact
        update = {'keys': ["mobile", "group"]}
4

4 回答 4

7

将较大的任务分成较小的部分。

第 1 步 - 只需读取 CSV 文件

ContactCSVModel.import_from_filename() 和 ContactCSVModel.import_from_file() 都返回 csv 行。禁用与 django 模型的交互以跳过与数据库的交互。这应该会大大加快任务并打印导入的数据。这绝对应该有效!

CSV模型

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"

你的代码

 def process(self):
        self.date_start_processing = timezone.now()
        try:


            # Try and import CSV
            lines = ContactCSVModel.import_data(data=self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])
            print lines # or use logging

            self._mark_processed(self.num_records)
        except Exception as e:
            self._mark_failed(unicode(e))

第 2 步 - 启用 django 模型交互但禁用以检查数据库中的现有项目。

禁用它,因为启用此功能将查询 CSV 中每一行的数据库,以根据您的自然键规范检查现有项目(我已阅读源代码)。可能您知道 CSV 中的所有行都是唯一联系人。

如果您的问题是整个导入过程中的数据库查询缓慢,这将有所帮助,但如果导入消耗太多内存,则并没有真正的帮助。

class ContactCSVModel(CsvModel):

    first_name = CharField()
    last_name = CharField()
    company = CharField()
    mobile = CharField()
    group = DjangoModelField(Group)
    contact_owner = DjangoModelField(User)


    class Meta:
        delimiter = "^"
        dbModel = Contact

第 3 步 - 导入相同大小的 CSV 块

使用 CSVModel 并启用与 Contact 模型的交互,但为 ContactCSVModel.import_data() 提供更小的迭代。我将其设置为 500。根据您的需要更改它。下面的代码示例(链接)是为了让您了解。您需要对其进行一些更改以将其放入现有代码中。如果内存消耗是问题,这将有所帮助。

import csv
reader = csv.reader(open(self.filepath, 'rb'))

def gen_chunks(reader, chunksize=100):
    """ 
    Chunk generator. Take a CSV `reader` and yield
    `chunksize` sized slices. 
    """
    chunk = []
    for i, line in enumerate(reader):
        if (i % chunksize == 0 and i > 0):
            yield chunk
            del chunk[:]
        chunk.append(line)
    yield chunk

for chunk in gen_chunks(reader, chunksize=500):
    ContactCSVModel.import_data(data=chunk, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

第 4 步 - 针对大量内存消耗和缓慢运行

因为 django-adaptors 在导入期间将所有 Contact 模型实例保存在内存中,并且由于多次单次提交而不是批量插入操作而导致操作缓慢 - 它不太适合较大的文件。

你有点依赖 django-adaptors。如果您依赖此 django 包,则无法切换到批量插入。在 linux 下使用 top 或 htop 检查内存消耗,在 windows 上使用任务管理器。如果进程消耗太多并且操作系统开始交换,请切换到另一个 django 附加组件,该附加组件具有更高效的内存消耗和批量插入作为选项 - 有很多用于 csv 导入。

另一个提示是使用 csv 模块进行阅读,并使用您的 django 模型知识与数据库进行交互。这对你来说并不是一个真正的挑战——只要尝试一下你的大局中的孤立任务,如果它们正在工作,就把它们放在一起——祝你好运。

于 2013-04-18T22:39:51.987 回答
2

首先要尝试的是向import_data函数传递一个可迭代对象:

ContactCSVModel.import_data(open(self.filepath), extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

要尝试的第二件事是使用import_from_filename

ContactCSVModel.import_from_filename(self.filepath, extra_fields=[
                {'value': self.group_id, 'position': 5},
                {'value': self.uploaded_by.id, 'position': 6}])

如果这没有帮助,请尝试找出它挂在哪里。您可以通过减小 csv 文件的大小来手动执行此操作,或者您可以在 上放置一个模拟csv.reader,或者您可以模拟CsvImporter.process_line而不是处理行,而是打印出它们以查看它在哪里停止。如果您在嘲笑方面需要帮助,请告诉我。

此外,这个问题可能是相关的。

于 2013-04-18T10:41:53.893 回答
2

我会首先检查 csv 中没有数据错误。例如,如果一列有错误的转义字符或不正确的数据类型 - 也许数据库不能接受某些列上的空值。

在挂起时,您可以手动检查数据库是否正在填充吗?通过命令行 MySQL 提示符或工作台?如果是,则自动提交已打开,您应该能够看到它挂在哪一行 - 然后检查 CSV 中的记录。

但是,如果关闭自动提交(我不知道 Django 默认会做什么,或者您的数据库是如何配置的),那么您可能会溢出事务缓冲区。应该有一种方法可以分阶段手动刷新/提交事务来解决这个问题。

于 2013-04-18T12:40:38.940 回答
2

我对 django-adaptors 了解不多,但是当数据库导入缓慢时,一些对我有帮助的事情是@transaction.commit_manually()在方法上使用装饰器,或者使用该Model.objects.bulk_create()方法。对您来说,看起来 commit_manually 方法可能会有所帮助,但 bulk_create 方法不会,因为您实际上并没有控制创建过程。

于 2013-04-18T17:55:49.870 回答