数据库:用以存储数据,这样之后应用可以再次面问。
高速缓存 缓存那些复杂或操作代价昂贵的结果,以加快下一次访问。
索引 用户可以按关键字搜索数据井支持各种过掳
流式处理:持续发送消息至另 个进程,处理采用异步方式。
批处理 定期处理大量的累积数据。
越来越多 应用系统需求广泛,单个组件往往无能满足所有数 处理与
求。因而需要将任务 解,每个组件负责高效完成其中 部分,多个组件依靠应用层
代码驱动有机衔接起来。
有必要从根本上思考下如何评价一个好数据系统,如何构建一个好的数据系统,有哪些可以遵循的设计模式?有哪些通常需要考虑的方面?
书中用了三个词来回答:可靠性(Reliability)、可扩展性(Scalability)、可维护性(Maintainability
出现意外情况如硬件、软件故障、人为失误等,系统应可以正常运转,虽然性能可能有所降低,但确保功能正确
如何衡量可靠性?
两个易混淆的概念:Fault(系统出现问题) 和 Failure(系统不能提供服务)
不能进行 Fault-tolerance 的系统,积累的 fault 多了,就很容易 failure。
哪些故障?
硬盘的平均无故障时间( MTTF )约为 10 50 年,如果你有 1w+ 硬盘,则均匀期望下,每天都有坏盘出现。当然事实是硬盘会一波一波坏。
应对办法:冗余。
对磁盘配置RAID ,服务器配备双电源,甚至热插拔 CPU ,数据中心添加备用电源、发电机等
单机:RAID冗余
多机:多副本
硬件故障属于随机性,而软件错误相关性高
在设计软件时,我们通常有一些环境假设,和一些隐性约束。随着时间的推移、系统的持续运行,如果这些假设不能够继续被满足;如果这些约束被后面维护者增加功能时所破坏;都有可能让一开始正常运行的系统,突然崩溃。
应对
软件是人设计和构建的,也是人来维护的。人为也是不可靠的
软件的不同阶段,可以有对应的应对措施
随着规模 增长 ,例如数据 、流量或复杂性,系统应以合理的方式来匹配这种增长
描述应对负载增长能力的指标
应对负载前,需要用合适的方式衡量负载,如负载参数
书中以 Twitter 2012 年 11 月披露的信息为例进行了说明:
单就这个数据量级来说,无论怎么设计都问题不大。但 Twitter 需要根据用户之间的关注与被关注关系来对数据进行多次处理。常见的有推拉两种方式:
前者是 Lazy 的,用户只有查看时才会去拉取,不会有无效计算和请求,但每次需要现算,呈现速度较慢。而且流量一大也扛不住。
后者事先算出视图,而不管用户看不看,呈现速度较快,但会引入很多无效请求。
如何描述系统性能
响应时间通常以百分位点来衡量,比如 p95,p99 和 p999,它们意味着 95%,99%或 99.9% 的请求都能在该阈值内完成。在实际中,通常使用滑动窗口滚动计算最近一段时间的响应时间分布,并通常以折线图或者柱状图进行呈现。
有了描述负载和衡量性能的指标,可以讨论可扩展性了:应对负载增加,如何保持良好性能?
升级更好的机器
用多个低廉机器分摊负载
负载扩展的两种方式:
针对不同应用场景:
首先,如果规模很小,尽量还是用性能好一点的机器,可以省去很多麻烦。
其次,可以上云,利用云的可扩展性。甚至如 Snowflake 等基础服务提供商也是 All In 云原生。
最后,实在不行再考虑自行设计可扩展的分布式架构。
两种服务类型:
随着时间的推移,许多新的人员参与到系统开发和运维, 以维护现有功能或适配新场景等,系统都应高效运转。
软件的开发成本不在最初的开发阶段,在于后续的持续投入
但是人们只喜欢挖坑,不喜欢填坑。因此可以在设计时,把控好三个原则:
方便运维团队来保持系统平稳运行。
有效的运维需要这些事情:
系统具有良好的可维护性,意味着将可定义的维护过程编写文档和工具以自动化,从而解放出人力关注更高价值事情:
复杂度表现:
需求很简单,但不妨碍你实现的很复杂:过多的引入了额外复杂度——非问题本身决定的,而由实现所引入的复杂度。
好的解决方法是抽象
复杂度高通常是问题理解的不够本质,写出了“流水账”(没有任何抽象,abstraction)式的代码。
如果你为一个问题找到了合适的抽象,那么问题就解决了一半,如:
如何找到合适的抽象?
总之,一个合适的抽象,要么是符合直觉的;要么是和你的读者共享上下文的。
系统不会一成不变,需求不断变化
应对: