如何设计谷歌阅读器等新闻提要系统?

时间:2016-03-29 07:29:47

标签: redis system software-design

我正在准备系统设计面试,我在面试时会被问到这样的问题,所以我想展示我的设计过程。另外,我想在这个过程中解决一些困难的最佳实践是什么。我想在可伸缩性方面考虑如何处理数据库上繁重的读写操作。如果我有任何想法,请纠正我。

首先,我想构建一个功能订阅 / 取消订阅。对于用户,我想设计标记提要已读/未读。我该如何设计这样的系统?乍一看,我可以看到的第一个问题是,如果我将每个数据都放在数据库中,一旦成千上万的用户订阅/取消订阅某些来源或媒体来源(如CNN帖子每5次),就会对数据库进行大量的读/写操作。 - 10分钟。

显然,一旦用户进入某一点,数据库就会成为瓶颈。 我怎样才能解决这个问题?解决这个问题的想法是什么?虽然从这个角度来看数据库是一个瓶颈,但我们仍然需要拥有数据库但设计更好吗?我看到很多文章谈论非规范化数据。

问题: 为每个来源存储订阅者的最佳方式是什么?

在数据库中,我可以认为一个表有“source_id”“user_id”,user_id订阅了source_id。这是一个好的设计还是坏的?如果大量用户订阅新源,那么数据库将成为负担。 方法我可以想到这是使用Redis,它提供快速写入和快速读取。 的优点

  • 快速读写操作。
  • 提供多个数据结构,而不是简单的键值存储。

缺点

  • 数据需要适合内存⇒解决方案到此:分片。诋毁我 可以使用twemproxy来管理集群。

  • 如果数据丢失,我们会丢失数据⇒解决方案:复制,拥抱 “主从”设置。写入主站,从从站读取和备份 数据到磁盘(数据持久性)。此外,每小时拍摄一次快照 基础。

现在我列出了迁移到redis集群的优缺点,如何在redis中存储源和订户之间的关系?如果我有一个散列图,将每个源和每个点存储到一个订户列表,这是一个好的设计。

例如,

Cnn⇒(sub1,sub2,sub3,sub4 ......) Espn⇒(sub1,sub2,sub3,sub4 ..) ...

在可扩展性方面,我们可以将每个源和用户分成每个专用的redis节点 << ==这至少是我现在能想到的。

此外,我们可以在redis中存储用户信息(用户订阅的内容),并将shard用户存储到多个群集

User1⇒(source1,source2,source4 ..) User2⇒(source1,source2,source4 ..) ...

对于来自单一来源的Feed和帖子,我可以同时拥有数据库表和redis数据结构(基本上,我的想法是将所有内容存储在redis和数据库中作为备份,在这种情况下它是一个很好的设计考虑因素吗?也许不是一切,只有redis或最近的Feed中的活跃用户)

数据库:我希望尽可能简洁,只存储它的副本。 feedID,sourceID,created_timestamp,data

Redis :存储feedID,source_id和内容,并根据source_id查找订阅者。

对于读/未读部分,我不清楚如何围绕这些限制进行设计。 每个用户都有加入时间戳,如果用户没有读取,服务器将推送源(每个源最多10个源)。用户是否读取数据的好设计是什么?我最初的想法是跟踪每个读取或未读取的用户。但是这张桌子可以线性增长到饲料的大小。在redis中,我可以设计类似的结构。

Userid,feedid,状态 User1,001,阅读 User1,002,阅读 User1,003,未读

此时,我最初设置数据结构的想法如上所述。 Redis每小时运行“主从”设置并备份到磁盘。

现在我要考虑订阅/取消订阅功能的过程如何运作。用户单击媒体页面上的订阅按钮,例如CNN。 Web服务器接收请求“用户X”订阅“源Y”。在应用层逻辑上,找到具有用户X数据的机器,这可以通过在每个应用服务器上安装分片映射来实现。像这样的user_id mod shard = machineid。

一旦应用程序查找具有其(用户X)数据的服务器ip,应用程序服务器就会与redis节点通信并使用新的source_id更新用户结构。订阅功能是一回事。

对于用户X 上的特定源的读取/未读取,应用程序将查找redis节点并更新其结构,并且redis异步更新数据库。 (这里我拥抱最终的一致性。)

让我们考虑如何设计推/拉模型。 对于推送通知,一旦有最近的提要,我可以用redis存储它最近的提要并仅更新活动用户(原因是尽可能避免对数据库进行尽可能多的写操作)。

对于拉模型,只有在用户重新加载其主页面时才更新用户,这也避免了大量的磁盘搜索时间。

有些观点:

  • 仅将活跃用户置于redis中(最近30天登录)
  • 如果用户未激活6个月,并且最近退回并且 想查看Feed。还有另一项服务重建数据 从数据库中放入redis并为用户服务。
  • 将最近的Feed存储在redis中,并仅将通知推送到活动状态 订阅者此时。这是为了避免磁盘寻道时间 数据库中。
  • 为了使Feed可排序,请在feedID中设计时间戳。对于 例如,feedID的前10位是时间戳,我们也可以 另外10位也用于ID中嵌入的sourceID。这使饲料 可分类的。
  • 应用程序服务器可以横向缩放并隐藏在后面 负载均衡器。
  • 应用程序服务器连接到redis群集,数据库用于 尽可能存储和重建数据(如非活动用户) 情况下)
  • Redis应用“主从”设置。写入主站,从从站读取 并异步复制数据。及时将数据备份到磁盘 基础。还异步更新数据库。

问题

  • 在新事件时使用redis异步更新数据库 一个可行的解决方案?或者只是保持复制 够了?

我知道这是一篇很长的帖子,想听听社区的回复。如果我错了,请纠正我,或者指出任何问题,以便我们讨论更多关于方法的问题。

0 个答案:

没有答案