Architecting Backend For Social Product

Goals

  1. 确保用户的内容总是很容易被发现的
  2. 确保推送的内容是相关的,不仅是在语义上,而且还从用户设备的角度来看
  3. 确保实时更新,推送,分析
  4. 尽可能节省用户资源
  5. 无论服务器负载,用户体验应保持不变
  6. 确保整体应用安全

总的来说,我们要解决一个巨大的挑战,我们要面对巨大海量并且实时增长的用户内容,越来越多的用户,永不间断的内容流,同时确保出色的性能.我们必须学习一些关键的架构元素,这将影响到系统设计.一下是一些关键的决定和分析.

Data Storage

数据存储和数据模型是一个关键决定.一个社交产品可以处理多种类型的数据,因此它是至关重要的,在我们分析理解数据之前,要为每种类型的数据选择一个模型和存储.

第一步是要确定哪些是最经常查询的数据,或者哪些不是经常要查询的数据(归档数据分析).对于高频率使用的数据必须是支持高可用的,同时支持高速读写和横向扩展.现在我们的实例都在使用MySQL,即使我们的用例并没有要求我们必须使用关系型数据库.随着数据的增长,读写将成为应用程序的性能瓶颈.我们应该为每秒数十亿的查询准备好.

现在把数据进行分类:

  1. 元数据,静态数据,比如用户配置文件
  2. 语义数据
  3. 用户生成的内容
  4. session数据

我们很难找到一个存储支持这么多种类型的数据并提供很好的性能.因此我们需要为每种不同的数据类型找到合适的存储.

Static Data

对于静态数据来说我们最好是选择基于文档的数据库,并且同时支持基于 键/值 的查询.可以选择基于文档的数据库比如MongoDB,其最大优势提供了文档级别的ACID.

它可以在多个分布式数据中心的范围内进行缩放,支持我们使用副本以保持很好的冗余,从而解决可用性问题.

分片是一个重要的考虑因素,是保证规模和速度的重要成分.

Connected or Relational Data (Core Data)

我们数据的主要部分将会自然连接.比如,A是B的朋友,C同时是A和B的朋友,这种高度语义的数据最适合被建模为 图.我们应该把这样的数据存储在类似Neo4j的图数据库.优势明显:它允许我们存储节点之间的关系与节点本身,从而省略了计算节点间连接关系的额外步骤.图数据库模型有助于我们捕捉到各属性的关系.当试图搜索相互连接的数据时,属性丰富的关系则成为绝对的关键.GraphDB支持ACID自动索引.

同时还要支持同样的可用性和扩展性.我们可以有成百上千的交易,同时成百上千的读取和写入.它的规模应该能够处理十亿每秒的数据集读取.

我们需要一个支持读写动态缩放的系统.需要考虑的其他因素是分片,这是我们系统的临界模型.

Neo4j已经设计了横向扩展和复制功能以保证高可用.但现在仍然不支持分片,我们需要更多的分析,然后选择一个最合适的.类似的可选图数据库还有FlockDB,AllegroGraph,InfiniteGraph.

Binary data (UGC)

我们也需要处理大量与用户相关的二进制数据.但处理二进制数据时很难考虑他们打大小.向上面讨论的,我们的系统需要一个很高的吞吐量,选择存储时规模和可用性是非常关键的因素.我们不能依靠本地的文件系统来存储二进制数据.必须要考虑可用性和可扩展性.比如亚马逊的S3,这是保证可用性非常流行的弹性对象存储.

我们还可以考虑谷歌的云存储和云文件托管,但S3在这里的优势很明显.S3已经支持数据分区,并需要注意的是,我们存储的数据都需要一个标识,以便他可以在其他地方进行索引.

Session Data

认识和理解我们要考虑会话数据的原因是非常重要的.会话数据将帮助我们保持用户状态.我们需要一种新的方式来更新用户的实际会话,如果用户的会话终止,我们仍然可以帮助用户从他上次断开的地方开始.

考虑纯粹的键值存储Redis.所有的推荐和离线作业都应有只运行在非服务节点上.

Indexing

索引使我们系统的关键.用户可以搜索任何内容,这是我们的主要用例之一.为了保证搜索性能,必须非常认真的对待索引.

做一个有意义的搜索系统必须设计一个实时指标. 考虑 Apache Storm 和 Apache Solr.

Queuing & Push Notifications

每次一个事件在我们的应用中被触发时,我们就需要向他的追随者/朋友等等发送各种形式的通知.重要的是,我们的系统不能错过任何这些信号,并且能够在发生故障后恢复这些事件.为了达到这种效果,我们必须找到一个队列解决方案.考虑ActiveMQ.

Caching Strategy

我们的系统目标是十亿RPS,智能缓存是最重要的.我们的设计需要多个层的逻辑缓存和智能淘汰.

Application Level Caching (Content Cache)

为了最大限度的减少缓存未命中,确保缓存始终是最新的的可用数据,我们必须寻找一个从未过期的缓存,并始终保持.这基本上意味着在我们一般使用的情况下,将永远不需要查询数据库,因此节省大量资源.我们还应该保证我们的缓存数据总是以一种不需要任何消息的格式,并且应该准备好呈现.这基本上意味着我们将在线负载转换为离线负载,从而节省了延迟.

要做到这些,我们必须保证每次数据被摄入系统,我们需要做的:

  1. 原内容不在服务状态做任何规范化处理,然后存储到缓存,设置足够的过期时间
  2. 原内容同时存储到数据库

我们完全可以依赖Redis作为缓存,它是一种具有良好的故障恢复的内存缓存.

Proxy Cache

在反向代理级别进行缓存也很重要.它有助于减少一些负载到我们的服务器,从而保持延迟.

Second Level Cache (Code Level Caching)

Client Cache

Content Compressions

使用gzip.

Content Transcoding

Transport Protocols

Security

Components

  1. Load Balancer
  2. Proxy Server
  3. Ingestion Engine
  4. REST Server
  5. Event Processor
  6. Recommendation Engine

Components

Origin: Architecting Backend For Social Product