MongoDB和Redis作为缓存层架构

时间:2014-06-26 05:33:19

标签: node.js mongodb caching redis

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

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

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

6 个答案:

答案 0 :(得分:16)

这实际上取决于你的需求,但这是一个相当常见的:

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

但是所有这些都假定低写入,高读取和稳定的查询集合。

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

答案 1 :(得分:5)

Idel方法是回写缓存方式。 你可以先编写mongodb,然后写入redis。这是最常见的方式。

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

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

答案 2 :(得分:5)

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

正如您可以从状态Feed的实现中看到the feed has option fanoutOnWrite cache,它将create a cache (limited size document)为活跃用户,限制缓存文档中最新条目的数量(该数字是可配置的)。

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

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

当用户登录时为用户读取缓存并且不在那里时,请回到“fanoutOnRead”(即查询他们关注的所有用户内容),然后从该结果中构建缓存。

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

答案 3 :(得分:2)

由于您的问题与架构有关,因此以"假设......"

开头

选择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

答案 4 :(得分:0)

需要一些严肃的数据才能使Redis成为MongoDB缓存层的可行选项,同时要记住MongoDB本身有一个工作集,它存放在RAM中;因此,如果您知道自己在做什么并正确规划您的架构,那么它们实际上可以从内存中提供服务。

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

  • 计数器
  • 斑点
  • 队列

以及更多,但你可以很容易地看到他们的memcached安装如何也可以与它合并以包括某些帖子,如果MongoDB是他们的主要商店而不是MySQL。

因此,演示文稿本身可以让您了解其他人如何将Redis与MongoDB一起使用。

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

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

答案 5 :(得分:0)

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

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

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

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

最后,有些人已经从mongo + redis迁移到像viber这样的couchbase,也许你应该认为这是一个选择? http://www.couchbase.com/viber