使用异常模式分组数据
如果您的集合存储的文档大小和形状大致相同,一个与预期截然不同的文档(异常)可能会对常见查询的性能造成问题。
考虑一个存储数组字段的集合。如果一个文档包含的数组元素比集合中的其他文档多得多,您可能需要在模式中对该文档进行不同的处理。
使用异常模式将不匹配预期形状的文档与集合的其他部分隔离开来。您的模式仍然保留了所有相同的数据,但常见查询不会受到单个大型文档的影响。
在开始之前
在修改您的架构以处理异常值之前,考虑异常值模式的优缺点
优点
异常值模式提高了常用查询的性能。返回典型文档的查询不需要也返回大异常值文档。
异常值模式还处理了应用中的边缘情况。例如,如果您的应用通常从一个数组中显示50个结果,就不会有一个包含2,000个结果的文档来破坏用户体验。
缺点
异常值模式需要更复杂的逻辑来处理更新。如果您经常需要更新数据,您可能需要考虑其他架构设计模式。更多信息请参见异常更新.
关于此任务
考虑一个跟踪书籍销售的架构。该集合中的典型文档看起来像这样
db.sales.insertOne( { "_id": 1, "title": "Invisible Cities", "year": 1972, "author": "Italo Calvino", "customers_purchased": [ "user00", "user01", "user02" ] } )
的customers_purchased
数组是 无界的,这意味着随着更多客户购买书籍,该数组会变大。对于大多数文档来说,这不会成问题,因为商店不期望特定书籍的销售量会超过几笔。
假设一本新出版的热门书籍导致了大量购买。当前的架构设计会导致文档膨胀,这会负面影响性能。为了解决这个问题,为那些没有典型销售量的文档实施异常模式。
步骤
结果
异常模式阻止非典型文档影响查询性能。结果架构避免了集合中的大文档,同时保持销售的全列表。
考虑一个显示一本书信息和购买此书的所有用户的页面。在实现异常模式后,页面可以快速显示大多数书(典型文档)的信息。
对于热门书籍(异常),应用程序在extra_sales
集合上针对book_id
执行额外查询。为了提高此查询的性能,您可以在book_id
字段上创建索引。
异常更新
您需要对异常文档的更新不同于典型文档。您用于执行更新的逻辑取决于您的架构设计。
为了更新前面架构中的异常,实现以下应用程序逻辑
检查正在更新的文档是否将
has_extras
设置为true
。如果
has_extras
不存在或为false
,将新购买的订单添加到sales
集合。如果结果
customers_purchased
数组包含超过50个元素,将has_extras
设置为true
。
如果
has_extras
为true
,将新购买的订单添加到对应book_id
的sales_extras
集合。