设计数据密集型应用程序 - 可靠 & 可扩展 & 可维护

设计数据密集型应用程序 - 可靠 & 可扩展 & 可维护

笔记来自于 《Designing Data-Intensive Applications》 的第一章

何为数据密集型应用程序

很多应用程序都需要用到如下和数据打交道的系统:

  • 数据库
  • 缓存
  • 搜索数据 & 索引
  • 流处理
  • 批量处理

设计这样的应用程序需要考虑很多因素,在此重点关注:

  • 可靠性: 系统持续工作
  • 可扩展: 能维持系统负载 (Load) 的增长
  • 可维护: 多人维护

Twitter 的负载

  • 2012 年 Tweet 平均产生的速率: 4.6k/s,峰值速率可以达到 12k/s.
  • 用户浏览首页的这个 API 请求平均: 300k/s.

Twitter 主要的挑战在于,每个用户可以关注很多人,每个人可以被很多人关注。实现这种系统通常有两种方式:

(1) 用户发布 Tweet 直接写入到大的 Tweet 表中即可。而用户浏览首页,需要首先查找用户关注的所有人,找到这些人发布的所有 Tweet,然后(按照时间)合并这些 Tweet:

SELECT tweets.*, users.* FROM tweets
 JOIN users ON tweets.sender_id = users.id
 JOIN follows ON follows.followee_id = users.id
 WHERE follows.follower_id = current_user

(2) 每个用户的 Timeline 都维护一个缓存。用户发布 Tweet 时,查找关注这个用户的所有人,然后将这条 Tweet 插入到这些人的 Timeline 中。这样用户浏览 Timeline 的请求,几乎不需要耗费什么代价,因为结果已经放置好了。

方法 (2) 的缺点是将 46k/s 写转为 345k/s 写,因为发布一条帖子,平均需要插入到 75 个关注者的 Timeline 中,更别提那些大 V 有千万的关注者,在几秒内写入这么多数据可以说是非常具有挑战性的。所以,对于 Twitter 这样的系统,每个用户的关注者的分布是一个非常核心的影响可扩展性的负载因子。你的应用程序或许有其它不同的特征参数,但是你运用上述分析原则也能分析出影响你的系统性能的关键因子

Twitter 现在的系统采用的是方法 (1) 和方法 (2) 混合在一起的方法,同时使用。

Hadoop 的吞吐量

Hadoop 的吞吐量是指每秒钟能处理的记录,但有时候响应时间(响应时间: 客户端衡量的;延迟: 一个请求等待多长时间被处理)是一个更好的衡量指标。你不要把响应时间看做是一个单一的值,而应该将其视为一系列可以测量的值的分布

这偶尔高出来的响应时间可能是因为后台进程的上下文切换、TCP 包的重传、垃圾收集器的 STW、缺页中断、服务器机架的物理震动或其他原因。

另外,你可能看过许多人提到平均响应时间,这并不是一个很好的衡量系统的指标,它并没有告诉你一个普通用户实际上感受到了多少 delay。更好的方法是使用 percentiles,将所有的响应时间由最快到最慢进行排序,取中位数这个数字,你可以很直观地了解到你的用户通常需要等待多长时间,这个中位数也称之为 50th percentile,有时候缩写为 p50。根据此种指标,你可以找出你的系统在极端情况下表现的有多差劲,比如改为 95th、99th、99.9th 等等。

响应时间的 High percentiles,也称之为tail latencies,是一个很重要的指标,因为它直接影响用户的体验。例如,亚马逊在内部设置了 99.9th 的指标,即使只影响 1/1000 的请求,这是因为发起请求最慢的用户通常是在账户里面拥有最多数据的用户,他们购买的产品也会更多,它们是最有价值的那批用户,如何保证这批用户欢快的购物体验是极其重要的,亚马逊也注意到响应时间每增加 100ms,销售额就会降低 1%,另外一项调查表明每增加 1秒钟的响应时间,用户的购物满意度降低 16%。

如果某个服务依赖了多个其他服务,那么被依赖的服务只要有一个服务的响应时间变慢,就会有相当一部分用户觉得整个系统变慢了,这称之为尾部延迟增强 (tail latency amplification)

监控响应时间的手段有很多种,比如可以使用滚动窗口实时统计过去 10 分钟之内的所有请求的响应时间的中位数,以及不同的 percentiles 指标。比较自然的实现方式是使用一个 List 每分钟排序一次找出这些指标,效率更为高效的实现方式可以采用 forward decay、t-digest、HdrHistogram 方法。

如何应对可扩展性

设计一个每秒钟需要处理 10W 请求,每个请求 1KB 的系统,和设计一个每分钟需要处理 3 个请求,每个请求 2GB 大小的系统,其设计应该是完全不同的。

银弹并不存在。

可维护性

三个系统设计原则:

  • Operability: Make it easy for operations teams to keep the system running smoothly.
  • Simplicity: 新加入工程师能够比较容易地理解系统.
  • Evolvability: 工程师能够在未来能够比较容易地改变系统.