《数据密集型应用系统设计》读书笔记——第一部分 数据系统基础

第一部分 数据系统基础

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

当今许多新型应用都属于数据密集型,而不是计算密集型。对于这些类型应用,CPU的处理能力往往不是第一限制性因素,关键在于数据量、数据的复杂度以及数据的快速和多变性。
数据密集型应用系统设计也是基于标准模块构建而成的,通常包含以下模块:

  • 数据库:用以存储数据,之后应用可以再次访问
  • 高速缓存:缓存那些复杂或操作代价高昂的结果,以加快下一次访问
  • 索引:用户可以按关键字搜索数据并支持各种过滤
  • 流式处理:持续发送消息至另外一个进程,处理通常采用异步方式
  • 批处理:定期处理大量的累积数据
    本章主要探讨所关注的核心目标:可靠性、可扩展性与可维护性的数据系统。澄清本源,解析处理之道。
    认识数据系统
    我们通常将数据库、队列、高速缓存等视为不同类型的系统,本书将它们归为一大类,即“数据系统”。原因有二:
  1. 近年来出了许多用于数据存储和处理的工具。他们针对各种不同的应用场景进行优化,不适合再归为传统类型。例如,Redis既可以用于数据存储也适用于消息队列,Apache Kafka作为消息队列也具备了持久化存储保证。系统之间的界限正在变得模糊。
  2. 越来越多的应用系统需求广泛,单个组件往往无法满足所有数据处理与存储需求。因而需要将任务分解,每个组件负责高效完成一部分,多个组件依靠应用层代码驱动有机衔接起来。
    常见应用的数据系统架构如下:
    《数据密集型应用系统设计》读书笔记——第一部分 数据系统基础_第1张图片

可靠性

当出现意外情况如硬件、软件故障、人为失误等,系统可以继续正常运转;虽然性能可能有所降低,但确保功能正确。
对于软件,典型的期望包括:

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

可能出错的事情成为错误或者故障,系统可应对错误则成为容错或者弹性。
注意,故障和失效不完全一致。故障通常被定义为组件偏离其正常规格,而失效意味着系统作为一个整体停止,无法向用户提供所需的服务。
在这种容错系统中,用于测试目的,可以故意提高故障发生概率,例如通过随机杀死某个进程,来确保系统仍保持健壮。Netflix的Chaos Monkey系统就是这种测试的典型例子。(混沌工程)

硬件故障

当我们考虑系统故障时,对于硬件故障总是很容易想到:硬盘崩溃,内存故障,电网停电,甚至有人误拔掉了网线。
我们的第一个反应通常是为硬件添加冗余来减少系统故障率。例如对磁盘配置RAID,服务器配置双电源,甚至热插拔CPU,数据中心添加备用电源、发电机等。
但是,随着数据量和应用计算需求的增加,更多的应用可以运行在大规模机器之上,随之而来的硬件故障率呈线性增长。因此,通过软件容错的方式来容忍多机失效成为新的手段,或者至少成为硬件容错的有利补充。这样的系统更具有便利性,例如可以用滚动升级的方式来给操作系统打补丁。
我们通常认为,硬件故障之间多是相互独立的。

软件错误

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

人为失误

设计和构建软件系统总是由人类完成,也是由人来运维这些系统。即使有时意图是好的,但人却无法做到万无一失。我们假定人是不可靠的。

可扩展性

随着规模的增长,例如数据量、流量或复杂性,系统应以合理的方式来匹配这种增长。
可扩展性是用来描述系统应对负载增加能力的术语。它并不是衡量一个系统的一维指标,谈论“X是可扩展”或“Y不可扩展”没有太大的意义。讨论可靠性通常要考虑这类问题:“如果系统以某种方式增长,我们应对增长的措施有哪些”,“我们该如何添加计算资源来处理额外的负载”。

描述负载

负载可以用成为负载参数的若干数字来描述。参数的最佳选择取决于系统的体系结构。它可能是Web服务器的每秒请求处理次数,数据库中写入的比例,聊天室的同事活动用户数量,缓存命中等。有时平均值很重要,有时系统瓶颈来自于少数峰值。

描述性能

在批处理系统如Hadoop中,我们通常关心吞吐量,或者在某指定数据集上运行作业所需的时间;而在线系统通常更看重服务的响应时间。
我们经常考察服务请求的平均响应时间。然而,如果想知道更典型的响应时间,平均值并不是合适的指标。最好使用百分位数,将收集到的响应时间按从快到慢排序,取前百分之X的数据,得到X百分位数,也叫pX(pctX),如p95(pct95)。

应对负载增加的方法

应对负载增加的常用方法有垂直扩展(升级到更强大的机器)和水平扩展(将负载分布到多个更小的机器)。
随着时间的推移,许多新的人员参与到系统开发和运维,以维护现有功能或适配新场景等,系统都应高效运转。
垂直扩展通常更简单,但高端机器可能非常昂贵,且存在上限,往往还是无法避免水平扩展。
把无状态服务扩展至多态机器相对比较容易,而有状态服务于的复杂性会大大增加。出于这个原因,通常的做法一直是,将数据库运行在一个节点上(采用垂直扩展策略),知道高扩展或高可用性的要求迫使不得不做水平扩展。
然而,随着相关分布式系统专门组件和变成接口越来越好,至少对于某些应用类型来说,上述通常做法或许会改变。可以乐观的设想,即使应用可能并不会处理大量数据或流量,但未来分布式数据系统将成为标配。

可维护性

众所周知,软件的大部分成本并不在最初的开发阶段,而是在于整个生命周期内持续的投入,这包括维护与缺陷修复,健康系统来保持正常运行、故障排查、适配型平台、搭配新场景、技术缺陷的完善以及增加新功能等。

可运维性:运维更轻松

运营团队对于保持软件系统顺利运行至关重要。一个优秀的运营团队通常至少负责以下内容:

  • 监视系统健康状况,并在服务出现异常状态时快速恢复服务
  • 追踪问题的原因
  • 保持软件和平台至最新状态
  • 了解不同系统如何互相影响
  • 预测未来可能的问题,并在问题发生之前着手解决
  • 执行复杂的维护任务,如平台迁移
  • 指定流程来规范操作行为
  • 保持相关知识的传承

良好的可操作性意味着日常工作变得简单,数据系统设计可以在这方面贡献很多,包括:

  • 提供对系统运行时行为和内部的客观策行
  • 支持自动化,与标准工具集成
  • 避免绑定特定的机器,允许停机维护
  • 提供良好的文档和易于理解的操作模式
  • 提供良好的默认配置,且允许方便的修改
  • 尝试自我修复
  • 行为可预测,减少意外发生
简单性:简化复杂度

简化系统设计并不意味着减少系统功能,而主要意味着消除意外方面的复杂性。
消除意外复杂性最好的手段之一是抽象。一个好的抽象设计,可以对外提供干净、易懂的接口;可以再不同应用程序中复用,复用远比多次实现更有效率;另一方面,也能带来更高质量的软件。

可演化性:易于改变

一成不变的系统需求几乎没有,想法和目标经常在不断变动。
我们的目标是可以轻松的修改数据系统,使其适应不断变化的需求,这和简单性与抽象性密切相关:简单易懂的系统往往比复杂的系统更容易修改。

第2章 数据模型与查询语言

数据模型可能是开发软件最重要的部分,他们不仅对软件的编写方式,而且还对如何思考待解决的问题都有深远的影响。

关系模型与文档模型

现在最著名的数据模型可能是SQL,它基于关系模型:数据被组织成关系,在SQL中成为表,其中每个关系都是元组的无序集合(在SQL中称为行)。
随着计算机变得越来越强大和网络化,服务目的的日益多样化,关系数据库已经从商业数据处理,顺利推广到了各种各样的用例。

NoSQL的诞生

进入21世纪,NoSQL成为推翻关系模式主导地位的又一个竞争者。现在很多新兴的数据库系统总是是打上NoSQL的标签,其含义通常被解释为“不仅仅是SQL”。
采用NoSQL数据库有这样几个驱动因素,包括:

  • 比关系型数据库更好的扩展性,包括支持超大数据集或超高写入吞吐量
  • 普遍偏爱免费和开源软件而不是商业数据库产品
  • 关系模型不能很好地支持一些特定的查询操作
  • 对关系模型一些限制性感到沮丧,渴望更具动态和表达力的数据模型

不同的应用程序有不同的需求,在可预见的未来,关系数据库仍将与各种非关系数据存储一起使用,这种思路又是也被称为混合持久化。

对象-关系不匹配

如果数据存储在关系表中,那么应用层代码中的对象与表、行和列的数据库模型之间需要一个笨拙的转换成。这种模型之间的脱离有时被称为阻抗失配。
对象-关系映射模型(ORM)框架减少了此转换成所需的样板代码量,但是它们并不能完全隐藏两个模型之间的差异。
《数据密集型应用系统设计》读书笔记——第一部分 数据系统基础_第2张图片

多对一和多对多关系

无论是存储ID还是存储文本字符串࿰

你可能感兴趣的:(Data)