Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >『数据密集型应用系统设计』读书笔记(一)

『数据密集型应用系统设计』读书笔记(一)

作者头像
1ess
发布于 2021-12-17 12:13:22
发布于 2021-12-17 12:13:22
6600
举报
文章被收录于专栏:0x7c00的专栏0x7c00的专栏

『数据密集型应用系统设计』读书笔记(一)

發佈於 2021-12-15

这本书一直在我的待读列表,但是一直没有机会拜读,直到最近 2021 年已经快要过去,感觉需要在年末提升一下自己。边读边做一下笔记,留待后用。

数据密集型与计算密集型

对于一个应用系统,如果”数据”是其成败决定性因素,包括数据的规模、数据的复杂度或者数据产生与变化的速率等,我们就可以称为”数据密集型(Data-Intensive)应用系统”。与之对应的是计算密集型(Compute-Intensive),CPU 主频往往是其最大的制约瓶颈。

内容安排

全书分为三大部分:

  1. 主要讨论有关增强数据密集型应用系统所需的若干基本原则
  2. 我们将从单机的数据存储转向跨机器的分布式系统,将依次讨论数据远程复制、数据分区、事务、分布式系统的更多细节以及分布式环境如何达成一致性与共识
  3. 主要针对产生派生数据的系统(所谓派生数据主要指在异构系统中,如果无法用一个数据源来解决所有问题,那么一种自然的方式就是集成多个不同的数据库、缓存模块以及索引模块)

可靠、可扩展与可维护的应用系统


第一章主要介绍相关术语与方法, 这些术语等将贯穿于全书。

  1. 可靠性: 当出现意外情况如硬件、软件故障、人为失误等,系统应可以继续正常运转,虽然性能可能有所降低,但确保功能正确
  2. 可扩展性: 随着规模的增长,例如数据量、流量或复杂性,系统应以合理的方式来匹配这种增长
  3. 可维护性: 随着时间的推移,许多新的入员参与到系统开发和运维,以维护现有功能或适配新场景等,系统都应高效运转

可靠性


  1. 应用程序执行用户所期望的功能
  2. 可以容忍用户出现错误或者不正确的软件使用方法
  3. 性能可以应对典型场景、合理负载压力和数据量
  4. 系统可防止任何未经授权的访问和滥用

可能出错的事情称为错误或故障,系统可应对错误则称为容错或弹性。 需要注意: 容错并不是指系统可以容忍各种可能的故降类型,而是指特定类型的故障。

在这种容错系统中,用于测试目的,可以故意提高故障发生概率,例如通过随机杀死某个进程,来确保系统仍保持健壮。通过这种故意引发故障的方式,来持续检验、测试系统的容错机制,增加对真实发生故障时应对的信心。

硬件故障

当我们考虑系统故障时,硬件故障包括: 硬盘崩溃,内存故障,电网停电,甚至误拔网线。应通常是为硬件添加冗余来减少系统故陪率。

软件错误

另一类故障则是系统内的软件问题,这些故障事先更加难以预料,而且因为节点之间是由软件关联的,因而往往会导致更多的系统故障。软件系统问题有时没有快速解决办法,而只能仔细考虑很多细节,包括认真检查依赖的假设条件与系统之间交互,进行全面的测试,进程隔离,允许进程崩溃并自动重启,反复评估,监控并分析生产环节的行为表现等。

人为失误

设计和构建软件系统总是由人类完成,也是由人来运维这些系统。人无法做到万无一失,例如,一项针对大型互联网服务的调查发现,运维者的配置错误居然是系统下线的首要原因。 如果我们假定入是不可靠的,那么该如何保证系统的可靠性呢,可以尝试结合以下多种方法:

  1. 以最小出错的方式来设计系统。例如,精心设计的抽象层、API 以及管理界面
  2. 想办法分离最容易出错的地方、容易引发故障的接口
  3. 充分的测试,从各单元测试到系统集成测试以及手动测试
  4. 当出现人为失误时,提供快速的恢复机制以尽最减少故障影响。例如,快速回滚配置改动,滚动发布新代码等
  5. 设置详细而清晰的监控子系统,包括性能指标和错误率
  6. 推行管理流程并加以培训

可靠性的重要性

很多应用都需要可靠工作,商业软件中的错误会导致效率下降,电子商务网站的暂停会对营收和声誉带来巨大损失。即使在所谓”非关键”应用中,我们也应秉持对用户负责的态度。

可扩展性

即使系统现在工作可靠,并不意味着它将来一定能够可靠运转。发生退化的一个常见原因是负载增加,服务一个客户和服务一万个客户,要处理的数据量也完全是几何级增长。

描述负载

首先,我们需要简洁地描述系统当前的负载,只有这样才能更好地讨论后续增长问题。负载可以用称为负载参数的若干数字来描述。参数的最佳选择取决于系统的体系结构:

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

有时平均值很重要,有时系统瓶颈来自于少数峰值。

描述性能

描述系统负载之后,接下来设想如果负载增加将会发生什么。有两种考虑方式:

  • 负载增加,但系统资源(如 CPU、内存、网络带宽等)保持不变,系统性能会发生什么变化
  • 负载增加,如果要保持性能不变,需要增加多少资源

在批处理系统如 Hadoop 中,我们通常关心吞吐量(throughput),即每秒可处理的记录条数,或者在某指定数据集上运行作业所需的总时间。 而在线系统通常更看重服务的响应时间(response time),即客户端从发送请求到接收响应之间的间隔。

注意: 我们经常考察的是服务请求的平均响应时间,然而,如果想知道更典型的响应时间,平均值并不是合适的指标。最好使用百分位数(percentiles)。如果已经搜集到了响应时间信息,将其从最快到最慢排序,中位数(median)就是列表中间的响应时间。中位数指标非常适合描述多少用户需要等待多长时间:一半的用户请求的服务时间少于中位数响应时间,另一半则多于中位数的时间。因此中位数也称为 50 百分位数,可缩写为 p5O。

当然为了弄清楚异常值有多槽糕,需要关注更大的百分位数如 95、99 和 99.9(缩写为 p95、p99 和 p999)值,作为典型的响应时间阈值。 采用较高的响应时间百分位数(长尾效应)很重要,因为它们直接影响用户的总体服务体验。例如,亚马逊采用 99.9 百分位数来定义其内部服务的响应时间标准,或许它仅影响 1000 个请求中的 1 个。但是考虑到请求最慢的客户往往是购买了更多的商品,因此数据量更大。换言之,他们是最有价值的客户。

对于后台服务,如果一次完整的服务里包含了多次请求调用,此时高百分位数指标尤为重要。即使这些子请求是并行发送、处理,但最终用户仍然需要等待最慢的那个调用完成才行。 最好将响应时间百分位数添加到服务系统监控中,持续跟踪该指标。例如,设置一个 lOmin 的滑动窗口,监控其中响应时间,滚动计算窗口中的中位数和各种百分位数,然后绘制性能图表。

应对负载增加的方法

我们已经讨论了描述负载的参数以及衡最性能的相关指标,接下来讨论可扩展性:即当负载参数增加时,应如何保持良好性能。

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

某些系统具有弹性特征,它可以自动检测负载增加,然后自动添加更多计算资源,而其他系统则是手动扩展。

可维护性

软件的大部分成本并不在最初的开发阶段,而是在于整个生命周期内持续的投入,这包括维护与缺陷修复。 不幸的是,许多从业人根本不喜欢维护这些所谓的遗留系统,为此,我们可以在软件设计时开始考虑,尽可能较少维护期间的麻烦,避免造出容易过期的系统。我们将特别关注软件系统的三个设计原则:

  1. 可运维性
  2. 简单性
  3. 可演化性

复杂性有各种各样的表现方式: 状态空间的膨胀、模块紧耦合、令入纠结的相互依赖关系、不一致的命名和术语、为了性能而采取的特殊处理、为解决某特定问题而引入的特殊框架等。 复杂性使得维护变得越来越困难,最终会导致预算超支和开发进度滞后。最终开发人员更加难以准确理解、评估或者更加容易忽略相关行为。 消除意外复杂性最好手段之一是抽象。一个好的设计抽象可以隐藏大量的实现细节,并对外提供干净、易懂的接口。一个好的设计抽象可用于各种不同的应用程序。

一成不变的系统需求几乎没有,想法和目标经常在不断变化: 适配新的外部环境、新的用例、业务优先级的变化、用户要求的新功能、新平台取代旧平台、业务增长促使架构的演变等。

总结

知易行难,我们都知道应该使应用程序可靠、可扩展或可维护,但实际实现他们却并不容易。考虑到一些重要的模式和技术在很多不同应用中普遍适用,在接下来的几章中,我们就一些数据密集系统例子,分析它们如何实现上述这些目标。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C语言笔记】如何查看数据类型范围?
1. 知识点一:查看整数范围 当前的编译环境下,你可能不知道int的数据范围是多少,或者记不清无符号短整型的范围是0~65535还是0~65536?这时候就可以按照如下程序进行输出查看: #inclu
正念君
2019/06/26
2.6K0
【C语言笔记】如何查看数据类型范围?
【C语言笔记】分享一个C语言测试程序模板
平时需要测试一些比较模糊的知识点,或则想要验证一些函数时,我们常常会建一个test.c文件,然后在这个文件里写我们的测试代码,测试完毕后常常会删掉该文件。下次再遇到同样的问题的时候,可能又是记不清楚了,常常又需要测试一遍,这是件很浪费时间的事情。
正念君
2019/06/26
2.8K0
【C语言笔记】分享一个C语言测试程序模板
C/C++魔法宏
1. 魔法宏 _LINE_ : 代表该行代码的所在行号; _FILE_ : 代表源文件的文件名; _DATE_ : 代表该源文件被编译的(月 日 年)日期格式; _TIME_ : 代表该源文件被编译的(时:分:秒)时间格式; _FUNCTION_ : 代表该源代码中插入当前所在函数名称; _STDC_ : 当该程序严格遵循ANSI C标准时该标识被赋值为1; __cplusplus:当编写的C++程序时该标识符被定义。 2. 示例 1 #include <iostream> 2 3 int main(int
Qt君
2019/07/15
5920
嵌入式软件开发常用的关键字和运算符
volatile是一个特征修饰符,提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。
不脱发的程序猿
2023/12/04
2540
C/C++代码调试:快速定位内存的申请和释放的位置
如果大型项目中出现类似于*** glibc detected *** logcacheinit: double free or corruption (fasttop): 0x00000000017db7f0 ***的错误。更糟糕的是项目既是多线程又是多个节点分布式运行的话,调试定位double free实在让人头痛。内核在程序崩溃的时候,这个信息只给出了被释放两次的内存地址,却没有给出程序出现两次内存释放的具体位置,这就需要我们自己动手排查。
恋喵大鲤鱼
2018/08/03
1.1K0
【C语言】预编译
main.c 6 Mar 15 2022 07:38:23 main 1
謓泽
2023/02/22
9990
【C语言】预编译
C语言打印程序行号、日期方便调试程序
平时开发C语言程序时,经常需要调试代码,C语言有一些宏,可以打印出当前的行号、文件名称、日期、时间,对程序的调试起到很大的帮助,可以快速定位问题。特别是开发单片机程序时,使用这些宏打印这些信息或者在LCD上显示程序的编译日期、时间,可以知道这个单片机上的固件是什么时候编译。帮助判断版本。
DS小龙哥
2022/06/17
2.2K0
C语言打印程序行号、日期方便调试程序
C语言从入门到实战——预处理详解
C语言预处理是C语言编译过程的一个阶段,它在编译之前对源代码进行一系列的处理操作,包括宏替换、文件包含、条件编译等,最终生成经过预处理的代码,然后再进行编译。
鲜于言悠
2024/03/20
7080
初识C语言·预处理详解
使用就是直接打印就好了,因为VS是不支持ANSI C标准的,支持的话返回值就是1
_lazy
2024/10/16
1180
初识C语言·预处理详解
编程这么久, 它们了解多少(一)
对于日志,一般情况下可以设置日志输出等级、输出到终端或文件、输出到每个文件的大小、日志被覆盖的策略,还有的可以在程序运行过程中更改日志的等级,或者将日志输出到远程服务器(至今没有接触到)等。
用户5908113
2020/11/09
4810
编程这么久, 它们了解多少(一)
一行代码就能写一个日志打印组件,你信吗?为你揭晓RTOS中日志打印组件的核心
在学习LiteOS日志打印组件使用的时候,我记录了一篇博客:atiny_log | LiteOS 物联网操作系统中的日志打印组件使用分享,关于实验的具体内容,请阅读这篇博客(点击阅读原文即可访问)。
Mculover666
2020/07/16
9460
一行代码就能写一个日志打印组件,你信吗?为你揭晓RTOS中日志打印组件的核心
c++/c 获取cpp文件行号跟文件名
编译器内置宏: 先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。 ANSI C标准中有几个标准预定义宏(也是常用的): __LINE__:在源代码中插入当前源代码行号; __FILE__:在源文件中插入当前源文件名; __DATE__:在源文件中插入当前的编译日期 __TIME__:在源文件中插入当前编译时间; __STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1; __cplusplus:当编写C++程序时该
hbbliyong
2018/03/06
1.9K0
C语言进阶(十五) - 预处理与程序编译初步解析
本节主要介绍程序运行前的预处理(预编译)阶段的相关知识。同时简单介绍一个程序是如何从一行行代码到开始运行并得到结果的。
怠惰的未禾
2023/04/27
4890
C语言进阶(十五) - 预处理与程序编译初步解析
预处理详解
我们知道switch 语句中,每一个case分支都需要在末尾加上break,才能真正实现分支,而我们有时候就很容易以往break,这时我们可以这样来写:
星辰与你
2024/10/17
1310
预处理详解
UIP协议栈移植到u-boot详解「建议收藏」
Author: 杨正 date:2014.11.5 Email:y2012ww@gmail.com QQ: 1209758756
全栈程序员站长
2022/11/03
1.3K0
C/C++总结
freopen("CONOUT$", "w", stdout);//重定向输出到控制台
用户7886150
2021/02/20
8530
预处理
这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息
DeROy
2020/05/11
8260
C语言 第八章 函数、指针与宏
该文介绍了如何在C++中实现一个简单的链表,包括链表节点的定义、基本操作的定义和链表的基本操作。同时,还介绍了在C++中如何实现链表,以及链表的一些常见应用场景。
张果
2018/01/03
9500
C语言 第八章 函数、指针与宏
Tool之预定义的宏
编译器都会预定义一些宏,这些宏是不可取消的,来看看VxWorks中可能用到的这几个
Taishan3721
2019/11/11
7790
Tool之预定义的宏
程序环境和预处理
在一个工程中,我们需要协作,那必须创建多个源文件(test.c),那么每个源文件经过编译器编译变为test.obj,再由链接器加链接库把test.obj文件变为test.exe可执行文件。 
The sky
2023/04/12
3790
程序环境和预处理
推荐阅读
相关推荐
【C语言笔记】如何查看数据类型范围?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档