【问题标题】:Aggregating/optimizing object.save()?聚合/优化 object.save()?
【发布时间】:2014-02-25 14:41:37
【问题描述】:

我正在研究允许用户从选定的 csv 文件创建 django 数据库模型的导入功能。

模型通过外键和多对多字段相互关联。 有很多

object.save()

Object.objects.get(...) 在我的代码中,我想这会导致它运行如此缓慢。

当发生错误(例如完整性错误)时,我需要回滚数据库中的所有更改。所以我正在使用

transaction.atomic 

装饰器在我看来,效果很好。

问题是,我的导入速度真的很慢。解析包含约 2000 行的文件(可能会在我的数据库中添加大约 1000 个对象)大约需要 3 分钟,这太长了。

有没有办法让它更快?我读过关于

bulk_create

函数,但“它不适用于多对多关系。”。

如果这很重要,我正在使用 postgresql。

编辑: 文件结构如下:

subject_name
day [A/B] begins_at - ends_at;lecturer_info  

然后是多行,如:

student_uid;student_info  

好的,这是代码。

def csv_import(market, csv_file):
    lines = [line.strip().decode('utf-8') for line in csv_file.readlines()]
    lines = [line for line in lines if line]
    pattern = re.compile(r'[0-9]+;.+')   

    week_days = {
        'monday': 0,
        .  
        .
        .
    }

    term, subject, lecturer, student = None, None, None, None

    for number, line in enumerate(lines):
        if not ';' in line:
            subject = Subject(subject_id=number, name=line, market=market)
            subject.save()
        elif not pattern.match(line):
            term_info, lecturer_info = line.split(';')  # term_info - 'day begins_at - ends_at', lecturer_info - lecturer
            term_info = term_info.replace(' - ', ' ').split()
            term = Term(term_id=number, subject=subject, day=week_days[term_info[0]], begin_at=term_info[-2],
                        ends_at=term_info[-1])

            if len(term_info) == 4:
                term.week = term_info[1]

            lecturer_info = lecturer_info.rsplit(' ', 1)
            try:
                lecturer = Lecturer.objects.get(first_name=lecturer_info[0], last_name=lecturer_info[1])
            except Lecturer.DoesNotExist:
                lecturer = Lecturer(first_name=lecturer_info[0], last_name=lecturer_info[1])
                lecturer.save()

            term.lecturer = lecturer

            term.save()
        else:
            gradebook_id, student_info = line.split(';')
            student_info = student_info.rsplit(' ', 1)
            try:
                student = TMUser.objects.get(uid=int(gradebook_id))
            except TMUser.DoesNotExist:
                student = TMUser(uid=int(gradebook_id), username='student'+gradebook_id, first_name=student_info[0],
                                 last_name=student_info[1], password=make_password('passwd'), user_group='user')
                student.save()
            student.terms.add(term)
            student.save()

【问题讨论】:

  • 哪位慢?解析文件或加载到数据库?你能展示一下你是如何加载它的吗?
  • 哦,对不起,我没有提到这个。我认为从数据库中保存/选择。一个没有运行分析器,但我从我的代码中删除了所有 django 的引用,使用简单的类而不是 django.models 并尝试解析相同的文件 - 然后花了几毫秒......如果你愿意,我可以编辑我的帖子和粘贴代码。
  • 代码会有所帮助,因为在不知道用例的情况下没有有效的答案
  • 我已经粘贴了代码,希望对你有帮助。
  • 基本上,您的代码会在不缓存结果的情况下进行大量查找。每次查询或创建 Term 或 Lecturer 时,将结果保存在 dict 中。然后,当您链接它们或创建其他东西时,您可以从字典中获取对象而无需额外的查询。

标签: python django postgresql


【解决方案1】:

这是一些伪代码,向您展示我所说的缓存结果的基本概念:

cache = {}

for number, line in enumerate(lines):
   ...
   elif not pattern.match(line):
      ...
      term = Term(term_id=number, subject=subject, ...)

      lecturer_id = (lecturer_info[0], lecturer_info[1])   #first name and last
      if cache[lecturer_id]:
         #retrieve from cache
         lecturer = cache[lecturer_id]
      else:
         try:
            lecturer = Lecturer.objects.get(first_name= lecturer_id[0], last_name= lecturer_id[1])
         except Lecturer.DoesNotExist:
            lecturer = Lecturer(first_name= lecturer_id[0], last_name= lecturer_id[1])
            lecturer.save()
         #add to cache
         cache[lecturer_id] = lecturer

      term.lecturer = lecturer
      term.save()   

      #etc.

【讨论】:

  • 我第一次了解你 ;) 我也会对用户这样做,因为他们重复了很多。但问题是——除此之外还有什么选择吗?我的意思是,一些优化此类任务的 django 功能?
  • 我对此表示怀疑。您正在这里优化数据库操作。批量操作可能仍然对您有所帮助,但您需要通过对 CSV 结果进行分组来对插入进行分组(您可以使用 Pandas 很好地做到这一点)。如果您随后可以使用连接查询,这可能会有所帮助,但在您的情况下可能不多。缓存您的结果,每次插入至少会优化一次往返。
  • 它仍然很慢,但它有点帮助,谢谢。
  • 在这种情况下,尝试将 CSV 解析为某种结构(字典、数据框等),然后对组进行查询/插入。如果失败,您将不得不查看您的数据库索引以查看它们是否已优化。
猜你喜欢
  • 2018-02-18
  • 2021-12-17
  • 2010-12-18
  • 2017-11-03
  • 2014-12-14
  • 1970-01-01
  • 2021-09-03
  • 2010-10-23
  • 1970-01-01
相关资源
最近更新 更多