假设我们有一个社交网络应用程序(使用NodeJS,Express)和MongoDB作为主数据库引擎.
在大多数来自客户端的API调用(移动应用程序,Web应用程序等)中,我不想为每个请求进行复杂的查询.例如,可以从缓存层(Redis)中回复这些类型的请求.
但我的问题是如何/何时更新缓存层,因为所有写操作都在MongoDB数据库中执行,而不是在缓存层(Redis)中执行.解决这个问题的正确方法/架构是什么?
Idel方法是回写缓存方式.你可以先编写mongodb,然后写入redis.这是最常见的方式.
另一种选择是,您可以先写redis并使用redis发送异步消息(如Q)某些线程可以使用该消息并读取它,将其写入mongoDB.
第一个选项更容易实现.第二个选项可以支持大量的写入事务.据我所知,mongodb锁定问题尚未解决(已从全局锁定修复到数据库级别锁定)第二个选项可能相当大,以减少此类锁定争用.
这已经在MongoDB 开源项目的名为"Socialite"的参考架构中实现,虽然它是在Java而不是node.js所以我的答案是基于我的经验压力和负载测试代码.
从状态源的实现中可以看出,feed有选项fanoutOnWrite缓存,它将为活动用户创建缓存(有限大小的文档),限制缓存文档中最新条目的数量(该数量是可配置的).
该实现的关键原则是内容要求实际上与时间线高速缓存要求不同,并且对内容数据库的写入首先是所有内容的记录系统,然后更新高速缓存(如果存在).如果需要,可以异步完成此部分.该更新利用"上限数组",即更新$ slice功能,以原子方式将新值/内容推送到阵列,同时关闭最旧的一个.
如果用户尚不存在,请不要为其创建缓存(如果他们从未登录,那么您就是在浪费精力).(可选)您可以根据某些TTL参数使缓存过期.
当用户在登录时为其读取缓存并且不在那里时,请回到"fanoutOnRead"(即查询他们关注的所有用户内容),然后根据该结果构建缓存.
Socialite项目将MongoDB用于所有后端,但在对其进行基准测试时,我们发现时间线缓存不需要复制或持久化,因此其MongoDB服务器仅配置为"内存"(没有日志,没有复制,没有磁盘)冲洗)这类似于你的Redis使用.如果丢失了缓存,它将"永久内容数据库""按需"重建.
这真的取决于你的需求,但这是一个相当普遍的:
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
但这一切都假定低写入,高读取和稳定的查询集.
你的高负载用例是什么样的?