代码之家  ›  专栏  ›  技术社区  ›  Bartek

django orm:以正确的方式组织大量数据

  •  1
  • Bartek  · 技术社区  · 14 年前

    我有一个django应用程序,它使用django活塞向内部客户机发送XML提要。一般来说,这些功能都很好,但是我们有一些XML提要,目前运行时间超过15分钟。这会导致超时,并且提要变得不可靠。

    我正在思考如何改进这个设置。如果它需要对数据进行一些重组,这也是可能的。

    以下是数据收集当前的外观:

    class Data(models.Model)
         # fields
    
    class MetadataItem(models.Model)
         data = models.ForeignKey(Data)
    
    # handlers.py
    data = Data.objects.filter(**kwargs)
    
    for d in data:
       for metaitem in d.metadataitem_set.all():
           # There is usually anywhere between 55 - 95 entries in this loop
           label = metaitem.get_label() # does some formatting here
           data_metadata[label] = metaitem.body
    

    显然,这个计划的核心是做更多的事情,但我只是指出问题所在。当我们有一个 data 300个列表只是变得不可靠和超时。

    我试过的:

    • 获取所有数据ID的集合,然后执行单个大型查询以获取 MetadataItem 最后,过滤掉我循环中的那些。这是为了保留一些它确实减少了的查询。
    • 使用 .values() 以减少模型实例开销,这确实加快了速度,但不会太快。

    一个想法 我正在考虑一个更简单的解决方案,就是分步骤写入缓存。所以为了减少超时,我会写前50个数据集,保存到缓存,调整一些计数器,写下50个,等等,仍然需要考虑这个问题。

    希望有人能帮我找到正确的方向。

    4 回复  |  直到 8 年前
        1
  •  2
  •   Bernhard Vallant    14 年前

    您发布的代码中的问题是django不包含自动通过反向关系连接的对象,因此您必须对每个对象进行查询。有个很好的方法可以解决这个问题,比如 Daniel Roseman points out in his blog !

    如果这不能很好地解决您的问题,您还可以看看如何在一个原始SQL查询中获取所有内容…

        2
  •  1
  •   knutin    14 年前

    您可以进一步减少查询计数,方法是先获取所有数据ID,然后使用select_related在单个大型查询中获取数据和元数据。这将大大减少查询的数量,但查询的大小可能不实际/太大。比如:

    data_ids = Data.objects.filter(**kwargs).values_list('id', flat = True)
    for i in data_ids:
        data = Data.objects.get(pk = i).select_related()
        # data.metadataitem_set.all() can now be called without quering the database
        for metaitem in data.metadataitem_set.all():
            # ...
    

    但是,如果可能的话,我建议从Web服务器之外的某个地方预计算提要。如果结果小于1 MB,也许可以将其存储在memcache中。或者,您可以是块上最酷的新孩子之一,并将结果存储在“nosql”数据库中,如redis。或者你可以把它写到磁盘上的一个文件中。

        3
  •  0
  •   knutin    14 年前

    如果您可以更改数据的结构,也许您还可以更改数据存储?

    “nosql”数据库允许一些结构,比如couchdb或mongodb,实际上在这里很有用。

    假设对于每个数据项都有一个文档。文档将具有您的普通字段。您还可以添加一个“元数据”字段,该字段是元数据列表。下面的数据结构是什么?

    {
        'id': 'someid',
        'field': 'value',
        'metadata': [
            { 'key': 'value' },
            { 'key': 'value' }
        ]
    }
    

    然后您就可以轻松地访问数据记录并获取所有的元数据。要进行搜索,请向“数据”文档中的字段添加索引。

    我在Erlang/OTP的一个系统中使用了mnesia,它基本上是一个带有索引和帮助器的键值数据库。我们大量使用嵌套记录,取得了巨大的成功。

    我把这个作为一个单独的答案添加,因为它与另一个完全不同。

        4
  •  0
  •   user338671    14 年前

    另一个想法是使用Celery(www.celeryproject.com),这是一个针对python和django的任务管理系统。您可以使用它异步执行任何长时间运行的任务,而不必占用主应用服务器。