【问题标题】:MongoDB and Redis as cache layer architectureMongoDB 和 Redis 作为缓存层架构
【发布时间】:2014-08-16 20:46:13
【问题描述】:

假设我们有一个社交网络应用程序(使用 NodeJS、Express)和 MongoDB 作为主要数据库引擎。

在来自客户端(移动应用、Web 应用等)的大多数 API 调用中,我不想对每个请求进行复杂的查询。这类请求可以从缓存层回复,例如 Redis。

但我的问题是我应该如何/何时更新缓存层,因为所有写入操作都在 MongoDB 数据库中执行,而不是在缓存层 (Redis) 中执行。解决此问题的正确方法/架构是什么?

【问题讨论】:

    标签: node.js mongodb caching redis


    【解决方案1】:

    这确实取决于您的需求,但这里有一个相当常见的:

    on_get_request
      if data_in_redis
        serve_data_from _redis
      else
        get_data_from_mongo
        set_data_in_redis
        set_expire_in_redis
        serve_data_from_memory
    

    数据有时会有点陈旧,但对于大多数用例来说没关系。当写入重要数据时,它与一些缓存失效结合使用效果很好:

    on_important_data
      delete_invalid_redis_keys
    

    但这一切都假设低写入、高读取和一组稳定的查询。

    您的高负载用例是什么样的?

    【讨论】:

    • 填充缓存的 cron 作业怎么样?
    • 如果 a) 可接受的过时时间很长并且 b) 读取时间很长,则可能会起作用。这并不少见,一个很好的例子是 mongo map reduce,十分钟前的结果就足够了,cron mongo map reduce 每十分钟更新一次 redis。
    【解决方案2】:

    这已经在 MongoDB open source project called "Socialite" 的参考架构中实现,尽管它是在 Java 中而不是 node.js 中,所以我的答案是基于我的经验压力和负载测试该代码。

    从它的状态提要实现中可以看出,the feed has option fanoutOnWrite cachecreate a cache (limited size document) 用于活动用户,限制缓存文档中最新条目的数量(该数量是可配置的)。

    该实现的关键原则是内容要求实际上与时间线缓存要求不同,首先写入内容数据库,因为这是所有内容的记录系统,然后更新缓存(如果存在)。这部分can be done asynchronously,如果需要的话。该更新利用“封顶数组”又名update $slice functionality 以原子方式将新值/内容推送到数组并同时删除最旧的值/内容。

    如果用户不存在缓存,请不要为它创建缓存(如果他们从未登录,那么您就是在浪费精力)。或者,您可以根据某些 TTL 参数使缓存过期。

    当您在用户登录时读取缓存但它不存在时,然后回退到“fanoutOnRead”(查询他们关注的用户的所有内容),然后根据该结果构建他们的缓存。

    Socialite 项目将 MongoDB 用于所有后端,但在对其进行基准测试时,我们发现时间线缓存不需要复制或持久化,因此其 MongoDB 服务器配置为仅“在内存中”(无日志,无复制,没有磁盘刷新),这类似于您的 Redis 使用。如果您丢失了缓存,它将“按需”从永久内容数据库中重建。

    【讨论】:

      【解决方案3】:

      Idel 方式是回写缓存方式。 可以先写mongodb,再写redis。这是最常见的方式。

      另一种选择是, 您可以先编写redis并使用redis发送异步消息(如Q) 一些线程可以消费消息并读取它,将其写入 mongoDB。

      第一个选项更容易实现。 第二个选项可以支持大量的写入事务。 据我所知,mongodb锁问题还没有解决(它已经从全局锁修复到数据库级锁) 第二个选项对于减少这种锁争用可能是相当重要的。

      【讨论】:

      • 实际测试表明,锁定不是插入内容的问题,因为通常磁盘 IO 最终会首先被最大化。只要以最佳方式完成架构和索引,每次插入的延迟都可以随着数据库/索引大小的增长而保持不变。
      【解决方案4】:

      因为您的问题是关于架构的,并且以“假设...”开头

      选择 mongoDB 有什么理由吗?

      使用 Postgres,我获得了比 mongoDB 更好的性能,并且通过 Postgres json/jsonb 支持获得了最好的关系和无模式文档,这实际上比 mongoDB 更快。使用 Postgres,您可以获得一个可靠的、久经考验的数据库,它具有出色的性能、可扩展性,最重要的是,您可以在晚上睡觉并享受假期。

      您还可以使用 postgres LISTEN/NOTIFY 来处理实时事件,以便执行 redis 缓存清除。

      这是在 nodejs 中使用 postgres LISTEN/NOTIFY 的示例: http://gonzalo123.com/2011/05/23/real-time-notifications-part-ii-now-with-node-js-and-socket-io/

      以下是 Postgres 9.4 作为无模式/noSQL 文档存储与 mongoDB 的一些综合性能基准:

      http://thebuild.com/presentations/pg-as-nosql-pgday-fosdem-2013.pdf

      【讨论】:

      • 你在“让你晚上睡觉,享受假期”引起了我的注意 :-)。也感谢您的链接。
      【解决方案5】:

      要使 Redis 成为 MongoDB 上的缓存层的可行选择,需要大量的数据,记住 MongoDB 本身有一个保存在 RAM 中的工作集;因此,如果您知道自己在做什么并正确规划架构,那么两者实际上都可以从记忆中服务。

      通常使用 Redis 进行缓存是 craigslist (http://www.slideshare.net/jzawodn/living-with-sql-and-nosql-at-craigslist-a-pragmatic-approach) 等大型网站的目标,正如您在该演示文稿的幻灯片 7 中看到的那样,这些网站将其用于:

      • 计数器
      • 斑点
      • 队列

      还有更多,但是如果 MongoDB 是他们的主要存储而不是 MySQL,您可以轻松地看到他们的 memcached 安装也可以与其合并以包含某些帖子。

      因此,该演示文稿本身可以让您很好地了解其他人如何将 Redis 与 MongoDB 结合使用。

      基本上,它通常用于保存从数据库中获取数据的速度通常太慢的数据快照。

      以下是一些相关信息,我将使用它们来稍微提高我的答案:What is Redis and what do I use it for?。我强烈建议您阅读该问题,因为它会让您更了解 Redis 的确切用例以及它可以做什么缓存。

      【讨论】:

      • 不要忘记,MongoDB 既复制数据,又将其作为日志和数据文件刷新到磁盘,这相对于纯内存解决方案而言会减慢速度。 MongoDB 可以配置为仅在内存中 - 请参阅我关于“Socialite”项目如何做到的回答。
      【解决方案6】:

      您需要事务和实时写入吗?当有人在 mongo 上编写更新时,是否绝对需要立即通知客户端更改(1 秒/分钟/天)?

      您的数据真的很重要,以至于任何写入都不应该丢失吗?如果是,则不能先在 redis 上写入,除非使用 AOF(这不是 redis 上的默认模式,而且速度要慢得多)。 例如,mongo 和 redis 之间的事务不会那么容易实现。

      如果先写redis,可以使用publish/subscribe通知redis客户端订阅更新mongo中的值,但不能保证你的数据安全传输,注意!但是,这应该是更新所有连接到 redis 的所有客户端的最快/最高效的方式。

      另一种方式,您可以在 redis 和 mongo 之间使用可接受的实时间隔定义轮询,以使用从 mongo 到 redis(解耦)的更改来更新缓存,而无需从您的代码直接写入 redis。您可以使用侦听器(mongo 中的“触发器”)来执行此操作或使用脏检查。

      最后,有些人已经从 mongo + redis 迁移到 viber 之类的 couchbase,也许您应该考虑使用它? http://www.couchbase.com/viber

      【讨论】:

      • 我只看了几行但 MongoDB 没有触发器
      猜你喜欢
      • 2013-04-20
      • 2018-05-25
      • 2012-06-28
      • 2014-03-10
      • 2020-04-22
      • 2021-10-13
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      相关资源
      最近更新 更多