锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

系统架构设计(3)-可扩展性

时间:2023-01-30 10:00:00 075k1l100只电阻器

即使现在系统可靠,也不代表以后会可靠。退化最常见的原因是负载增加:并发用户从最初的10000 增长到 目前,100、000或系统的数据处理量是以前的很多倍。

可扩展性,描述系统应对负载增加的能力。它不是衡量系统的一维指标, 谈系统X可扩展 或者不扩展没什么意义。相反,讨论可扩展性通常需要考虑:如果系统以某种方式增长,应对措施是什么? 如何添加计算资源来处理额外的负载

3.1 描述负载

为了更好地讨论后续的增长问题(例如,加倍负载意味着什么?)。负载可以用几个称为负载参数的数字来描述。参数的最佳选择取决于系统的系统结构,可能是:

  • 请求处理服务器的次数/s
  • 数据库中写入的比例
  • 聊天室同时活动用户数量
  • 缓存命中率

有时平均值很重要,但有时系统瓶颈来自少数峰值(特别是在促销期)。Twitter两个典型的业务操作:

  • 推文:用户可以快速向所有粉丝推送新消息,平均约4.6k request/s, 峰值约12k requests/sec
  • 页时间线(Home timeline)浏览:平均300k requests/s 查看关注对象的最新消息

仅处理峰值12k新闻发送似乎并不难,但扩展性挑战的重点不是新闻的大小,而是巨大的风扇( fanout,电子工程术语,描述输入逻辑门连接到另一个输出门的数量。输出需要提供足够的电流来驱动所有连接的输入。在事务处理系统中,用于描述为服务输入请求而需要做的请求总数)结构:每个用户都会关注很多人,被很多人圈起来。

对此有以下几点

处理方案

方案一:关系型数据模型

将发送的新推文插入整个推文集。当用户查看时间线时,首先找到所有的关注对象,列出这些人的所有推文,并按时间顺序排序合并。如果使用以下关系数据模型来支持时间:

可执行SQL:

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 
方案二:数据流水线万式推送

保持缓存每个用户的时间线 ,类似于每个用户的推文电子邮件。当用户推送新的推文时,查询他们的关注者,并将推文插入每个关注者的时间线缓存中。因为结果已经提前取出,然后访问时间是线性性能,非常快。

Twitter最初使用方案1,但发现主页时间线阅读负载压力日益增加,系统优化道路曲折,所以转向方案2,实践证明更好,因为时间线浏览推文压力几乎高于两个数量级,基本上,在发布时完成更多的事情可以加速阅读性能。事实上,方案2的缺点也很明显,正在发布tweet增加大量额外工作。平均考虑75名关注者和4名关注者.6k/s的tweet,则需每秒4.6*75 = 345k速率写入缓存。然而,平均关注者背后隐藏着其他事实,即关注者实际上有很大的偏差。例如,一些用户拥有3万多个w的追随者。这意味着在高峰情况下tweet会导致3000w次写入!而且要求尽量快, Twitte目标是5s内部完成,成为一个巨大的挑战!

本案可扩展的关键负载参数是每个用户关注者的分布(也可以结合用户使用推特的频率进行加权),因为它决定了风扇的数量。您的应用程序可能有不同的特性,但可以使用类似的原则来研究特定的负载。

Twitter最后,方案二稳定实现, Twitter它正在转向两种解决方案的组合。大多数用户的推文在发布时继续以一对多的方式写入时间线,但除了少数大V用户,他们的推文是单独提取的,只有在阅读时才与用户的时间线相结合。这种混合方案可以提供良好的性能。

3.2 描述性能

描述系统负载后,想象如果负载增加会发生什么。有两种考虑方法:

  • 负载增加,但系统资源(如CPU 、内存、网络带宽等。)保持不变,系统性能如何变化?
  • 为了保持性能不变,需要增加多少资源?

注意性能指标。

如批处理系统Hadoop ,通常关心吞吐量(throughput),也就是说,在指定的数据集上运行所需的每秒记录或总时间。在线系统通常更注重服务响应时间( response time),即客户端从发送请求到接收响应之间的间隔。

延迟( latency )与晌应时间( response time)

容易混淆的概念并不完全相同。

  • 响应时间是客户端看到的 :除请求时间(服务时间)外, service time )此外,还包括来回网络延迟和各种排队延迟

  • 延迟,请求在处理上花费时间

即使反复发送和处理相同的请求,也可能每次都有轻微不同的响应时间。响应时间可能会发生很大变化,因为系统需要处理各种不同的请求。因此,最好不要将响应时间视为固定数字,而是测量值分布。

大多数请求确实很快,但偶尔会出现异常,需要更长的时间。其中一些异常请求确实很昂贵,比如数据要大得多。但有时,即使所有的请求都是相同的,也会引入随机的延迟抖动,如上下文切换和过程调度、网络数据包丢失和TCP重传、垃圾回收暂停、缺页中断和磁盘I/O ,甚至服务器机架的机械振动。

平均响应时间

服务请求的平均响应时间不是合适的指标 ,由于掩盖了一些信息,无法解释有多少用户实际经历了多少延迟。最好使用百分比(percentiles) 。

百分数(percentiles)

如果收集响应时间信息,按最快至最慢排序,若中位数响应时间为200ms ,这意味着一半的请求响应不到200 ms ,而另一半请求需更长时间。用户需要等多久才能描述中位数指标: 一半用户要求的服务时间少于中位数响应时间,另一半多于中位数。所以中位数也叫50%,缩写为p50 。中位数对应于单个请求,这意味着如果用户发送多个请求(例如,它包含在一个完整的会话过程中,或者因为一个页面包含多个资源)

至少一个比中位数慢的概率远远大于50%。为了找出异常值,需要注意更大的百分位数,如常见的95、99、99.9 (缩写为p95、p99、p999 )分别表示95%、99%和99%.9%的请求响应时间快于阈值。 即使95%的响应时间是1.5s ,100个请求中的95个请求快于1.5s,而个请求需要1.5或更长时间。

响应时间百分位数较高( tail latencies,由于直接影响用户的整体服务体验,尾部延迟或长尾效应非常重要。如果亚马逊使用99.内部服务的响应时间标准定义为9百分位数,或许只影响1000个请求中的一个。但考虑到要求最慢的客户往往购买更多的商品,数据量更大。换言之, 他们是最有价值的客户。保持这些顾客愉快的购物体验不是很重要吗?亚马逊还注意到,响应时间每次增加l100ms ,其他研究表明,销售额将下降约1%, 1s延迟增加等于客户满意度下降6%。

有人说优化这99.99百分位数(10000个请求中最慢的一个)ROI 过低,技术成本进一步提高响应时间,容易受到随机事件等不可控因素的影响,累积优势会减弱。

例如,百分位数通常用于描述和定义服务质量目标( Service Level Objectives, SLO )协议服务质量( Service Level Agreement, SLA ),这些是规定服务预期质量和可用性的合同。例如一份SLA合约,通常会声明响应时间中位数小于200ms,99%请求的响应时间小于1s,且要求至少99.9%的时间都要达到上述服务指标。这些指标明确了服务质量预期,并允许客户在不符合SLA的情况下进行赔偿。

排队延迟往往在高百分数响应时间中影响大。由于服务器并行处理的请求有限(CPU核心数限制),正在处理的少数请求可能会阻塞后续请求,这种情况有时称为队头阻塞。即使后续请求可能处理简单,但它阻塞在等待先前请求的完成,客户端将会观察到极慢响应时间。因此,很重要的一点是要在客户端来测量响应时间。

所以,为了测试系统的可扩展性而人为地产生负载时,负载生成端要独立于响应时间来持续

发送请求。若客户端在发送请求之前总是等待先前请求的完成,就会在测试中人为缩短服务器端的累计队列深度,带来测试偏差。

3.3 应对负载增加的方案

现在真正讨论可扩展性了,当负载参数增加时, 如何继续保持良好性能呢。

实践中的百分位数

后台服务,若一次完整的服务包含多次请求调用,此时高百分位数指标尤为重要。 即使这些子请求是并行发送、处理,但最终用户仍然需等待最慢的那个调用完成。如下图 ,哪怕1个缓慢的请求处理,即可拖累整个服务。

即使只有很小百分比的请求缓慢,若某用户总是频 产生这种调用 ,最终总休变慢的概率就会增加(即长尾效应)。

最好将响应时间百分位数添加到服务系统监控 ,持续跟踪该指标。如设一个20min滑动窗口,监控其中的响应时间,滚动计算窗口中的中位数和各种百分位数,然后绘制性能图。一种简单的实现方案:在时间窗口内保留所有请求的响应时间列表,每分钟做1次排序。若这种方式效率太低,可采用一些近似法(如正向表减、t-digest或HdrHistogram)来计算百分位数,其CPU和内存开销很低。同时注意,降低采样时间精度或直接组合来自多台机器的数据,在数学上没有太大意义,聚合响应时间的正确方法是采用直方图。

针对特定级别负载而设计的架构不大可能应付超出预设目标10倍的实际负载。若目标服务处于快速增长阶段,则需要认真考虑每增一个数量级的负载,架构应如如何设计。

现在谈论更多的是如何在垂直扩展(升级更强大机器)和水平扩展(将负载分布到多个更小机器)之间取舍。在多台机器上分配负载也被称为无共享体系结构。在单台机器上运行的系统通常更简单,而高端机器昂贵,且扩展水平有限,所以无法避免需要水平扩展。好架构通常要做取舍,例如,使用几个强悍服务器仍可以比大量小型虚拟机来得更简单、便宜。

某些系统具有弹性特征,自动检测负载增加,然后自动添加更多计算资惊,而其他系统则得手动扩展(人工分析性能表现,之后再决定是否添加)。若负载高度不可预测,则自动弹性系统会更高效 ,但或许手动能减少执行期间的意外情况。

  • 无状态服务分布然后扩展至多台机器相对比较容易

  • 有状态服务从单节点扩展到分布式多机环境的复杂性会大大增加

    因此,直到最近通常的做法一直是,将数据库运行在一个节点(采用垂直扩展策略),直到高扩展性或高可用性的要求迫使不得不做水平扩展。

而随分布式系统发展,至少对某些应用,上述通常做法或许会改变。乐观地说 ,即使应用可能并不会处理大量数据或流量,但未来分布式数据系统将成为标配。

超大规模系统往往针对特定应用而高度定制,很难有通用架构。背后取舍因素包括数据读取量、写入量、待存储的数据量、数据复杂度、 响应时间要求、访问模式等或更多的是上述所有因素叠加,再加上其他更复杂问题。

例如,即使两系统数据吞吐量折算后一样,但为每秒处理100,000 次请求(每个大小为1KB )而设计的系统,和为3个请求/min(每个大小2GB )设计的系统大不相同。

对特定应用来说,扩展能力好的架构通常会做出某些假设,然后有针对性地优化设计,如哪些操作最频繁,哪些负载是少数情况。若这些假设最终发现是错误的,则可扩展性的努力就白费了,甚至会出现与设计预期完全相反情况。对初创公司或尚未定型产品,快速迭代推出产品功能往往比技入精力来应对不可知的扩展性更重要。

可扩展架构一般从通用模块逐步构建而来,背后往往有规律可循,所以我们会多讨论这些通用模块和常见模式。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章