本文最后更新于:星期二, 八月 2日 2022, 9:32 晚上
第一章:可靠、可扩展和可维护的应用系统
数据密集型问题的挑战:
数据量,数据复杂度,数据的快速多变性;
数据密集型应用包含以下模块:
- 数据库;
- 高速缓存;
- 索引:用户可以按关键字搜索数据井支持各种过滤操作;
- 流式处理:持续发送消息至另一个进程,处理采用异步方式;
- 批处理:定期处理大量累计数据。
数据系统
本章提出数据系统的概念,包括数据库、消息队列和高速缓存等不同类型的系统。这样分类的原因有以下几点:
都能将数据保存一段时间。区别在于访问模式不同,以及由于不同访问模式导致的不同性能。
新技术、工具拥有多种功能,系统之间的界限变得模糊。比如redis既能存储也能作为消息队列,kafka作为消息队列也能持久化存储。
系统也需要细分,原有的但各组件无法满足数据处理与存储需求,三分类概念需要进一步分解。例如,即便是一个简单的数据系统,也可能由很多子组件构成:
这个应用包含缓存层(Memcached)和全文索引服务器(Elasticsearch或者Solr)以及数据库。程序员编写应用代码来控制缓存、索引和数据同步操作。
数据系统的三种关键特性:
可靠性(Reliability):当出现意外情况,如软件、硬件故障,人为操作失误等现象时,系统仍可以正常运转的能力。这里的正常运转,是指牺牲部分性能条件下,提供正确的服务。
可扩展性(Scalability):随着问题规模的增长,比如流量、数据量或者任务复杂度,系统以合理方式匹配这种增长的能力。
可维护性(Maintainability):新的开发或者维护人员上手的容易程度,以及适配新场景的能力。
可靠性
什么是可靠性?即使发生了某些错误,系统仍可以继续正常工作的能力。
- 故障或错误(fault):组件偏离正确规格的现象。
- 失效(failure):系统宕机,无法提供服务。
为保证可靠性,需要设计容错机制(fault-tolerant)来避免故障引发系统失效,而不是避免故障(当然,避免故障也是提升可靠性的手段)。
可靠性指标
失效前平均时间(MTTF)
是针对不可修复系统而言的,是指系统发生失效前的平均工作(或存储) 时间或工作次数。越高越好。
平均无故障时间(MTBF)
是针对可修复系统而言的,指两次相邻失效(故障) 之间的工作时间, 而不是指整个系统的报废时间。越高越好。
平均修复时间(MTTR)
是对可修复系统而言的,指从出现故障到修复中间的这段时间。越低越好。
可用性
又称为有效性,一般用可用度来定量计算,公式为
可靠性通常低于可用性。对于不可维修系统, 可用度就仅仅决定于且等于可靠度。
数据系统可能发生哪些故障?如何提升容错能力?
1. 硬件故障
包括硬盘错误、电源错误、网口接触不良等。
最耐用的硬盘的MTTF为10~50年,因此一个包括10000个磁盘的存储集群中,一天坏一个也是很正常的(假设购买硬盘在时间上是均匀分布)。
应对方案是添加冗余,比如上述硬盘问题就可以用RAID,电源用双电源,甚至热插拔CPU等。
以上是从硬件角度解决硬件故障,其实可以通过软件角度解决硬件故障问题。
2. 软件错误
操作系统内核的Bug,系统依赖的服务突然没有响应,某个组件的失控等。
软件系统的问题有时没有快速解决的办法,只能通过检查依赖假设和系统交互、进行全面测试等方法来预防,或者允许进程重启、评估运行时表现等应对。
3. 人为失误
人是最不可靠的因素。运维人员配置错误往往是系统下线的主要原因。
这部分的预防以及应对,更加依赖软件工程领域的知识。
可扩展性
可扩展性是用来描述系统应对负载增加的能力的术语。一个系统添加计算单元越容易,添加计算单元后计算能力的增长越多,就称这个系统的可扩展性越强。
如何度量系统负载
每秒请求处理次数、数据库中写入的比例、聊天室的同时活动用户数量、缓存命中率等,
还要注意分析平均值和峰值对性能的影响
实例:twitter的两个实现版本
twitter有两个功能(类比微博,为了方便理解我接下来以微博代替推特),根据其2012年的数据,其功能和负载如下:
发微博:平均每秒4600条发布申请,峰值12000条推特申请发布。
收微博:平均每秒300,000条收微博的请求。
twitter第一个版本使用如下系统设计完成微博的收发:
发送微博:用户发送的微博插入全局微博集合。
用户申请查看自己的时间线:
- 遍历所有User的关注对象;
- 提取所有关注对象的微博;
- 按照发表时间排序并合并。
随着注册用户变多,负载压力与日俱增,因此采取第二种方法:
每个用户的时间线维护一个缓存。
用户发表新的微博:
- 查询关注对象;
- 插入到每个粉丝的时间线缓存。
方法二的好处是,用户发布微博时多做一些事情可以加速用户接收微博时的性能。而用户接收微博的请求负载比发送负载高两个数量级。
Twitter针对那些粉丝量特别多的大V采用方法一,针对粉丝量不太大的绝大多数用户使用方法二。
如何度量系统性能
吞吐量
吞吐量是在一个特定时间段内完成的任务的计数,例如:每秒点击数。
系统吞吐量几个重要参数:QPS(TPS)、并发数、响应时间
并发数: 系统同时处理的request/事务数
TPS:Transactions Per Second(每秒传输的事物处理个数),即服务器每秒处理的事务数。
QPS:每秒查询率,即对一个特定的查询服务器在规定时间内所处理流量大小。
响应时间
从客户端发送请求到接受响应之间的间隔时长。在线系统通常更看重响应时间,批处理系统更关心吞吐量。
由于每次请求的响应时间服从一个分布,因此我们更关心平均响应时长,或者百分位数时长。将响应时长排序,取50百分位数(中位数)比平均数更有意义。其他有意义的百分位数有95、99和99.9百分位数。
采用较高的响应时间百分位数很重要,因为他们直接影响用户的总体服务体验。95百分位数的响应时间为1.5秒,意味着100个请求中5个请求慢于1.5秒,但是对于电商平台来说,有可能恰恰是这些顾客购买了更多的商品导致访问变慢。
排队延迟是影响高百分数响应时间的主要延迟来源。因此,如果在性能测试时,负载生成客户端在服务器处理完之前请求后再发送新的请求,就会忽视了排队造成的延迟。
长尾效应
一个服务涉及多个不同的后端调用,则最慢的调用会拖累整个服务的响应时间,这种现象称之为长尾效应。
用户总是需要等待最慢的那个调用完成。因此即便只有很小比例的请求缓慢,也可能由于某一个用户频繁产生这种调用而导致总体变慢。
如何应对负载增加,提升可扩展性?
垂直扩展:升级到更强大的机器。
水平扩展:将负载分布到更多小机器。
系统设计时,要在这两种扩展中间作取舍。同时要根据不同的吞吐量、请求方式等做针对性优化。
可维护性
软件工程师不喜欢读别人留下的代码,不喜欢维护别人开发的老系统,这已经是众所周知的事实。
但是换个角度,不如说大多数系统在设计之初就没有考虑令后续开发者方便地维护这一个特性。
为了提升系统的可维护性,减少维护期间的麻烦,可以遵循软件系统的三个设计原则:
可运维性、简单性、可演化性。
可运维性
目标是令运维人员更轻松。之前讨论过,人是系统中最不稳定的因素。因此简化运维人员的操作,使运维人员能够专注于高附加值的任务,能够提升系统的可维护性。
可以从以下角度提升可运维性:
- 提供系统运行时监控工具,方便监控;
- 自动化工具;
- 避免绑定特定的机器;
- 提供良好的文档;
- 提供良好的默认配置;
- 尝试自我修复等。
简单性
简单性是复杂性的反面。而复杂性有各种各样的表现方式,比如模块紧耦合,状态空间膨胀,依赖关系复杂,命名方法混乱,各种性能trick等。
消除复杂性的最好手段之一是抽象,通过抽象掩盖大量实现细节,提供干净的接口。
可演化性
提升可演化性是令系统易于改变的另一种说法。目标是可以轻松地修改数据系统,使其适应不断变化的需求。
在组织流程方面,敏捷开发模式为适应变化提供了很好的参考。因此敏捷性与可演化性很类似。
notes Big Data Distributed System
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!