Goals
- 确保用户的内容总是很容易被发现的
- 确保推送的内容是相关的,不仅是在语义上,而且还从用户设备的角度来看
- 确保实时更新,推送,分析
- 尽可能节省用户资源
- 无论服务器负载,用户体验应保持不变
- 确保整体应用安全
总的来说,我们要解决一个巨大的挑战,我们要面对巨大海量并且实时增长的用户内容,越来越多的用户,永不间断的内容流,同时确保出色的性能.我们必须学习一些关键的架构元素,这将影响到系统设计.一下是一些关键的决定和分析.
Data Storage
数据存储和数据模型是一个关键决定.一个社交产品可以处理多种类型的数据,因此它是至关重要的,在我们分析理解数据之前,要为每种类型的数据选择一个模型和存储.
第一步是要确定哪些是最经常查询的数据,或者哪些不是经常要查询的数据(归档数据分析).对于高频率使用的数据必须是支持高可用的,同时支持高速读写和横向扩展.现在我们的实例都在使用MySQL,即使我们的用例并没有要求我们必须使用关系型数据库.随着数据的增长,读写将成为应用程序的性能瓶颈.我们应该为每秒数十亿的查询准备好.
现在把数据进行分类:
- 元数据,静态数据,比如用户配置文件
- 语义数据
- 用户生成的内容
- 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)
为了最大限度的减少缓存未命中,确保缓存始终是最新的的可用数据,我们必须寻找一个从未过期的缓存,并始终保持.这基本上意味着在我们一般使用的情况下,将永远不需要查询数据库,因此节省大量资源.我们还应该保证我们的缓存数据总是以一种不需要任何消息的格式,并且应该准备好呈现.这基本上意味着我们将在线负载转换为离线负载,从而节省了延迟.
要做到这些,我们必须保证每次数据被摄入系统,我们需要做的:
- 原内容不在服务状态做任何规范化处理,然后存储到缓存,设置足够的过期时间
- 原内容同时存储到数据库
我们完全可以依赖Redis作为缓存,它是一种具有良好的故障恢复的内存缓存.
Proxy Cache
在反向代理级别进行缓存也很重要.它有助于减少一些负载到我们的服务器,从而保持延迟.
Second Level Cache (Code Level Caching)
Client Cache
Content Compressions
使用gzip.
Content Transcoding
Transport Protocols
Security
Components
- Load Balancer
- Proxy Server
- Ingestion Engine
- REST Server
- Event Processor
- Recommendation Engine