gpt4 book ai didi

django - 使用 mongoengine 减少对 MongoDB 的调用次数

转载 作者:行者123 更新时间:2023-12-02 05:19:58 24 4
gpt4 key购买 nike

我正在努力优化一个(主要)由 MongoDB 支持的 Django 应用程序。它在负载测试下快要死了。在当前有问题的页面上,New Relic 显示对 pymongo.collection:Collection.find 的调用超过 700 次。 .大部分代码是由初级程序员编写的,通常我会寻找添加索引、进行更智能的连接和删除循环以减少查询调用的地方,但这里不能选择连接。我所做的(在添加基于 EXPLAIN 的索引之后)试图通过进行一般查询然后过滤循环中的较小集合来降低循环中的成本*。虽然我从 900 个查询中得到了这个数字,但即使在页面上完成了大量的工作,700 个仍然看起来很疯狂。我想也许 find即使在过滤现有查询集时也被调用,但代码表明它始终是 database query .

我已经向 mongoengine 添加了一些日志记录,以查看查询的来源并查看 EXPLAIN 语句,但是我没有太多的运气来筛选信息墙。 mongoengine 本身似乎是性能问题的一部分:我切换到 mongomallard作为测试并在页面上获得了 50% 的性能提升。不幸的是,我在一堆其他页面上遇到了错误(据我所知,Mallard 在过滤现有查询集时表现不佳;该错误提示在生成器中发生对 deepcopy 的调用,您可以不做——我在那里撞了一堵砖墙)。虽然 Mallard 对我们来说似乎不是一个可行的替代品,但它确实表明很多处理时间都花在了在 mongoengine 中将对象与 Python 相互转换。

我可以做些什么来进一步减少通话?还是我专注于错误的事情,应该在其他地方解决问题?

编辑:提供一些代码/模型

有问题的页面显示类(class)的教学大纲,显示类(class)中的所有模块、它们的类(class)以及类(class)下的概念。对于每个概念,还显示了用户在概念中的进展。所以有很多循环来梳理层次结构(并且它不是根据任何 patterns the Mongo docs suggest 存储的)。

class CourseVersion(Document):
...
course_instances = ListField(ReferenceField('CourseInstance'))
courseware_containers = ListField(EmbeddedDocumentField('CoursewareContainer'))

class CoursewareContainer(EmbeddedDocument):
id = UUIDField(required=True, binary=False, default=uuid.uuid4)
....
courseware_containers = ListField(EmbeddedDocumentField('self'))
teaching_element_instances = ListField(StringField())

类(class)的模块、类(class)和概念存储在 courseware_containers ;我们需要得到所有的概念,这样我们才能得到 teaching_element_instances 中的 id 列表。查找用户最近为该概念所做的工作(如果有的话),然后查看他们的进度。

*为了清楚起见,我正在使用分析器并根据我所知道的最正确的方式查看时间和做事,而不是简单地改变事情并希望最好。

最佳答案

代码示例本身还不错,但是应该考虑许多领域,并且可能有助于提高性能。

class CourseVersion(Document):
...
course_instances = ListField(ReferenceField('CourseInstance'))
courseware_containers = ListField(EmbeddedDocumentField('CoursewareContainer'))

class CoursewareContainer(EmbeddedDocument):
id = UUIDField(required=True, binary=False, default=uuid.uuid4)
....
courseware_containers = ListField(EmbeddedDocumentField('self'))
teaching_element_instances = ListField(StringField())

点评
  • 无限列表。course_instances , courseware_containers , teaching_element_instances
    如果这些字段是无限的并且持续增长,那么文档将随着它的增长而在磁盘上移动,从而导致负载较重的系统上的磁盘争用。有两种模式可以帮助减少这种情况:

    一)Turn on Power of two sizes .这会消耗磁盘空间,但随着文档的增长应该会降低 io churn 的数量

    b) Initial Padding - 在插入时自定义填充文档,以便将其放入更大范围,然后删除填充。真的是一种反模式,但它可能会给你一些里程。

    最后一个障碍是最大文档大小 - 16MB,您不能将数据增长得比这更大。
  • 引用字段列表 - course_instances
    MongoDB 没有连接,因此查找 ReferenceField 需要额外的查询。 - 本质上它们是应用内加入。这本身并不坏,但理解权衡很重要。默认情况下,mongoengine 不会自动取消引用仅执行 course_version.course_instances 的字段它会做另一个查询,然后填充整个引用列表。所以它可能会花费您另一个查询 - 如果您不需要数据,那么 exclude()它从查询中停止任何泄漏的查询。
  • 嵌入式字段

    这些字段是文档的一部分,因此除了传输和加载数据的有线成本外,它们没有任何成本。 **由于它们是文档的一部分,因此您不需要 select_related获取此数据。
  • teaching_element_instances
    这些是 id 的列表吗?它说它是 StringField在上面的代码示例中。无论哪种方式,如果您不需要取消引用整个列表,则存储 _ids作为 StringField如果编码正确,手动取消引用可能会更有效 - 特别是如果您只需要最新的(最后一个?)id。
  • 模型复杂度
    CoursewareContainer很复杂。对于任何给定的 CourseVersion你有 n CoursewareContainers自己有一个列表n容器和那些每个都有 n容器等等...
  • 查找最近的实例

    We need to get all of the concepts so we can get the list of ids in teaching_element_instances to find the most recent one the user has worked on (if any) for that concept and then look up their progress.



    我不确定您是否有一个实例,或者每个容器一个或每个类(class)一个。无论哪种方式 - 都应该检查查询数据的逻辑。如果它是您所追求的单个实例 - 那么可以针对用户存储它,以便简化查找逻辑。如果它是每个类(class)或容器,那么为了提高性能,请确保您最大限度地减少查询数量 - 如果可能,收集所有 ids然后在最后发出一个 $in查询,而不是对每个容器进行查询。
  • Mongoengine 成本

    目前,将数据加载到 Mongoengine 类中会产生性能成本 - 如果您不需要这些类并且乐于使用简单的字典,则发出原始 pymongo 查询或使用 as_pymongo .
  • 架构设计

    该模式看起来足够合乎逻辑,但它是否适合用例 - 本质上是它使用了 MongoDB 的优势,还是将关系 Hook 放在文档数据库形状的洞中?我不能回答你,但我确实知道 MongoDB 的幸福之路是根据其用例设计模式。对于关系数据库,架构设计从一开始就很简单——您可以规范化,对于文档数据库,数据的使用方式是主要因素。
  • MongoDB 最佳实践

    还有许多其他最佳实践,mongodb 有一个可能感兴趣的指南:MongoDB Operations Best Practices .

  • 请随时通过 Mongoengine mailing list 与我联系进一步讨论,如果需要私下讨论。

    罗斯

    关于django - 使用 mongoengine 减少对 MongoDB 的调用次数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23828288/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com