猫鼬填充缓存

时间:2020-01-22 20:06:40

标签: node.js mongoose

我有许多通过id字符串互相引用的猫鼬模式。

我正在使用Redis缓存猫鼬文档。

例如,getUser(id)将返回先前缓存的用户对象(如果存在),否则将调用猫鼬查找。

取而代之的是猫鼬引用并使用填充。

但是,据我了解,它只是用于查找的语法糖,并且没有任何缓存层。

主要问题

何时应该使用猫鼬填充而不是缓存层?使用猫鼬在稳定的高流量应用程序中的最佳做法是什么?

指导性子问题

  1. 猫鼬真的适合高流量应用吗?
  2. 使用填充而不是缓存文档有什么好处 我自己?
  3. 在性能方面微不足道的自我缓存模型(例如,使用redis)是可以忽略的吗?
  4. 最佳做法是什么?大型应用程式公司会使用什么 猫鼬呢?
  5. 您会混合填充猫鼬引用和缓存层吗 取决于不同的用例,还是您选择一个并成为 符合吗?

用例示例

这是我应用中的一个普通示例。

我有3个收藏集:用户,应用,研究所。

  1. 用户具有对应用的引用
  2. App推荐了Institute

现在我是:

  1. 从包含app_id的缓存层获取用户
  2. 从缓存层中获取应用,其中包含Institute_id
  3. 从缓存层获取研究所

给用户一个信息,从缓存层获取应用程序并进行初始化实际上是O(1)。

但是,如果我选择填充纯猫鼬,则会对数据库进行2次额外的查找调用-对于应用程序,然后对于研究所。

我需要具有应用程序的用户,并在对服务器的每个经过身份验证的请求上填充研究所。

当然还有更复杂的用例,但这是最常见的用例。

我最简单的请求平均需要填充4个引用,而更复杂的请求可以填充更多引用。

1 个答案:

答案 0 :(得分:1)

这是我对两者的利弊的理解。

猫鼬的优点

  • 无需额外设置缓存(简化的基础架构)
  • 它可以深度填充(以多个级别填充)
  • 它可以从多个数据库填充。
  • 这是一种简单的干净语法
  • 缓存和数据库之间不需要同步,因为它是“单个”事实来源。

猫鼬的缺点

  • 该数据库适用于每个填充和查询,而不是服务器或缓存层。如果在同一实例上进行大量写操作,则在需要重新计算索引或进行处理器密集型查询的情况下,这将影响某些写操作的性能。
  • 依靠mongoose和MongoDB数据库的内部工作。
  • 需要控制,因为深度填充可以在多个层次上失控。

用于缓存图层的优点

  • 可以是多个级别的缓存。每个服务器一些,并具有全局缓存。
  • 使用缓存引擎的特定作用力。
  • 将一些工作转移到缓存中,并可能转移到数据库中。

缓存层的缺点

  • 需要在缓存和数据库之间同步状态
  • 更多基础设施。
  • 更多代码(如果您想要简洁的抽象)

总体回答您的子问题, 1.对于某些流量较高的应用程序,“填充”可能有用,因为它们不能被缓存并且需要实时运行,或者不经常执行。

  1. 在缓存中使用填充更简单,更少的基础设施,更少的代码,没有同步。

  2. 根据我的经验,我会去缓存,因为在大型数据库上它会更快。扩展数据库时,往往需要更多的cpu并花费更多的钱。另一方面,缓存更便宜并且可以扩展。同样,可以按实例缓存。即,我的服务器在访问远程缓存之前具有本地缓存​​。这样可以使性能非常快,但是根据托管情况,可能会影响服务器性能。

  3. 我不是大公司,但是我们的产品需要交易信息和固定状态。在这种情况下可以使用填充,因为数据库是唯一的事实来源,而且我们不希望状态不正确。由于我们数据库的复制,它不是单一来源,但至少我们将接近数据库。我们在其他任何地方都使用缓存。我们有多个数据库和多个数据库类型,缓存使我们拥有更高的性能。我们的面向微服务的体系结构还从缓存中受益匪浅,并确保数据不是全部都在同一个数据库中,而是仍然可以快速访问。

  4. 是的,根据使用情况,混合是一个不错的选择。一个一般性的技巧是了解潜在的热点,并尝试分散工作量,以确保基础结构的一部分不会成为瓶颈。

最后的提示:毫无疑问,请确保在数据层和代码层之间保留代码接口。如果需要使用ElasticSearch代替Redis或任何其他缓存服务,则此抽象非常有用。代码界面将推迟作出承诺的需要。

示例:不是在一段代码中直接使用App.populate,而是在您的模式中添加调用getFullApp()的方法this.populate()


const AppSchema = new mongoose.Schema({...});

AppSchema.static({
   getFullApp(query) {
      return this.find(query).populate()
   }
})

module.exports = mongoose.model("App", AppSchema);

如果要摆脱填充,只有一个地方可以更改它或摆脱猫鼬getFullApp是您的代码接口的功能。