如果数据不断变化,你会缓存什么? (以Twitter为例)

时间:2012-07-24 07:10:26

标签: database caching memcached redis

我花了一些时间研究缓存(主要是redis和memcached),并且在数据不断变化时很难确定使用缓存的确切位置。

以Twitter为例(只需阅读Making Twitter 10000% faster)。当大部分数据库记录不断变化时,您(或他们)如何缓存数据?

说Twitter有这些模型:UserTweetFollowFavorite

某人可能会发布一条推文,该推文在一天内被转发一次,而另一条推文则在一天内转发了一千次。对于那个1000x转推,因为一天大约24 * 60 == 1440分钟,这意味着Tweet几乎每分钟都更新一次(比如它也有440个收藏)。跟随某人一样,查理谢恩甚至吸引了1 million Twitter followers in 1 day。在这些情况下缓存似乎并不值得,但也许只是因为我尚未达到这个水平。

还要说一般的Twitter粉丝每天至少发一次推文/关注/收藏。这意味着在天真的intro-rails架构案例中,users表每天至少更新一次(tweet_count等)。这种情况对缓存用户配置文件很有意义。

但对于上面的1000x Tweets和1M粉丝示例,在缓存数据方面的推荐做法是什么?

具体来说(假设使用memcached或redis,并使用纯JSON API(无页面/片段缓存)):

  • 您是否缓存单个推文/记录?
  • 或者您是否通过分页缓存记录块(例如每个20的redis列表?)
  • 或者您是单独还是在页面中缓存记录(查看单个推文与JSON Feed)?
  • 或者您是否为每个不同的场景缓存推文列表:家庭时间线推文,用户推文,用户喜欢的推文等?或者以上所有?
  • 或者您是将数据分解为“最不稳定(最新)”到“最近几天”到“旧”块,其中“旧”数据缓存的有效日期较长或者是离散的分页列表还是什么?并且最新的记录根本没有缓存。 (即如果数据与推文一样具有时间依赖性,如果您的旧记录知道它不会发生太大变化,您会以不同的方式对待它吗?)

我不明白的是数据变化的比例与缓存的比例(以及处理缓存到期的复杂性)。看起来Twitter可以缓存不同的用户推文提要,而家庭推文每个用户,但是每次收藏/推文/转推时都会使缓存失效意味着更新所有这些缓存项目(和可能缓存的记录列表),在某些时候似乎意味着使缓存失效会产生反作用。

缓存数据的推荐策略有哪些变化?

2 个答案:

答案 0 :(得分:3)

不是说Twitter就是这样做的(虽然我很确定它是相关的),但是:我最近熟悉 CQRS + 事件采购。 (http://martinfowler.com/bliki/CQRS.html + http://martinfowler.com/eaaDev/EventSourcing.html)。

基本上:读取和写入在应用程序和持久性级别(CQRS)上完全分离,并且对系统的每次写入都作为可订阅的事件(事件源)进行处理。还有更多内容(例如能够重放整个事件流,这对于以后实现新功能非常有用),但这是相关部分。

在此之后,通常的做法是每当负责的Read Model(即:它将事件投射到新的读取模型)接收到Projector时,就会重新创建RecentTweetsPerUserProjector(想想内存缓存)它订阅的事件类型的新事件。

在这种情况下,一个事件可能是TweetHandled,由所有订阅者处理,其中TimelinePerUserProjector,{{1}}等更新其各自的ReadModel。

结果是一组ReadModel,这些ReadModel最终是一致的,不需要任何失效,即:更新的写入和结果事件是更新ReadModel的触发器。

我同意最终Charlie Sheen的Read Model会得到更新(尽管这种更新可能非常有效),因此缓存优势可能非常低。但是,看一般用户的每个时间单位的平均发布量,并且图片完全不同。

DDD / CQRS /事件采购场景中的一些有影响力的人:Greg Young,Udi Dahan。

这些概念非常“深刻”,所以不要指望在一小时内完全理解它(至少我没有)。也许最近关于相关概念的思维导图也很有用:http://www.mindmeister.com/de/181195534/cqrs-ddd-links

是的,如果你没有注意到,我对此非常热心。)

答案 1 :(得分:0)

我的谦虚2美分:Redis允许您对其数据结构进行操作,这意味着您可以比每次触摸关系数据库更快地执行内存操作。

因此,“缓存”可以被更改,因此它不会像您期望的那样失效。

在我的项目中,我定期将500K记录加载到已排序的集合,然后仅通过对它们执行范围查询来运行统计报告,这使得报告执行时间平均低于2秒。