DDIA读书笔记-第一章

数据系统

常见的数据系统

数据库:用以存储数据,这样之后应用可以再次面问。

高速缓存:缓存那些复杂或操作代价昂贵的结果,以加快下一次访问。

索引:用户可以按关键字搜索数据井支持各种过掳

流式处理:持续发送消息至另 个进程,处理采用异步方式。

批处理:定期处理大量的累积数据。

越来越多应用系统需求广泛,单个组件往往无能满足所有数据处理要求。因而需要将任务分解,每个组件负责高效完成其中部分,多个组件依靠应用层代码驱动有机衔接起来。

有必要从根本上思考下如何评价一个好数据系统,如何构建一个好的数据系统,有哪些可以遵循的设计模式?有哪些通常需要考虑的方面?

书中用了三个词来回答:可靠性(Reliability)、可扩展性(Scalability)、可维护性(Maintainability

可靠性

出现意外情况如硬件、软件故障、人为失误等,系统应可以正常运转,虽然性能可能有所降低,但确保功能正确

如何衡量可靠性?

  • 用户期望的功能
  • 容忍用户不当使用方法
  • 性能可应对典型场景,合理的负载
  • 阻止未授权,恶意破坏

两个易混淆的概念:Fault(系统出现问题)Failure(系统不能提供服务)

不能进行 Fault-tolerance 的系统,积累的 fault 多了,就很容易 failure。

硬件故障

哪些故障?

  • 硬盘崩溃
  • 内存故障
  • 机房掉电
  • 网络不通
  • 机器过热导致cpu故障

硬盘的平均无故障时间( MTTF )约为 10 50 年,如果你有 1w+ 硬盘,则均匀期望下,每天都有坏盘出现。当然事实是硬盘会一波一波坏。

应对办法:冗余。

对磁盘配置RAID ,服务器配备双电源,甚至热插拔 CPU ,数据中心添加备用电源、发电机等

单机:RAID冗余

多机:多副本

软件错误

硬件故障属于随机性,而软件错误相关性高

  • 无法处理特定输入,导致崩溃
  • 错误进程耗尽共享资源,如cpu、内存、网络资源
  • 系统依赖的组件服务变慢甚至无响应
  • 级联故障

在设计软件时,我们通常有一些环境假设 ,和一些隐性约束。随着时间的推移、系统的持续运行,如果这些假设不能够继续被满足;如果这些约束被后面维护者增加功能时所破坏;都有可能让一开始正常运行的系统,突然崩溃。

应对

  • 检查依赖的假设条件与系统之间的交互
  • 全面测试
  • 进程隔离
  • 自动重启
  • 反复评估
  • 监控并分析
  • 异常告警

人为失误

软件是人设计和构建的,也是人来维护的。人为也是不可靠的

软件的不同阶段,可以有对应的应对措施

  • 设计编码
  1. 尽可能消除所有不必要的假设,提供合理的抽象,仔细设计 API
  2. 进程间进行隔离,对尤其容易出错的模块使用沙箱机制
  3. 对服务依赖进行熔断设计
  • 测试阶段
  1. 尽可能引入第三方成员测试,尽量将测试平台自动化
  2. 单元测试、集成测试、e2e 测试、混沌测试
  • 运行阶段
  1. 详细的仪表盘
  2. 持续自检
  3. 报警机制
  4. 问题预案
  • 针对组织
  1. 科学的培训和管理

可扩展性

随着规模 增长 ,例如数据 、流量或复杂性,系统应以合理的方式来匹配这种增长

描述应对负载增长能力的指标

描述负载

应对负载前,需要用合适的方式衡量负载,如负载参数

  • 应用日活月活
  • 每秒向 Web 服务器发出的请求
  • 数据库中的读写比率
  • 聊天室中同时活跃的用户数量

书中以 Twitter 2012 年 11 月披露的信息为例进行了说明:

  1. 识别主营业务:发布推文、首页 Feed 流。
  2. 确定其请求量级:发布推文(平均 4.6k 请求/秒,峰值超过 12k 请求/秒),查看其他人推文(300k 请求/秒)

单就这个数据量级来说,无论怎么设计都问题不大。但 Twitter 需要根据用户之间的关注与被关注关系来对数据进行多次处理。常见的有推拉两种方式:

  1. 。每个人查看其首页 Feed 流时,从数据库现拉取所有关注用户推文,合并后呈现。
  1. 。为每个用户保存一个 Feed 流视图,当用户发推文时,将其插入所有关注者 Feed 流视图中。

前者是 Lazy 的,用户只有查看时才会去拉取,不会有无效计算和请求,但每次需要现算,呈现速度较慢。而且流量一大也扛不住。

后者事先算出视图,而不管用户看不看,呈现速度较快,但会引入很多无效请求。

描述性能

如何描述系统性能

  1. 吞吐量(throughput):每秒可以处理的单位数据量,通常记为 QPS。
  2. 响应时间(response time):从用户侧观察到的发出请求到收到回复的时间。
  3. 延迟(latency):日常中,延迟经常和响应时间混用指代响应时间;但严格来说,延迟只是指请求过程中排队等休眠时间,虽然其在响应时间中一般占大头;但只有我们把请求真正处理耗时认为是瞬时,延迟才能等同于响应时间。

响应时间通常以百分位点来衡量,比如 p95,p99 和 p999,它们意味着 95%,99%或 99.9% 的请求都能在该阈值内完成。在实际中,通常使用滑动窗口滚动计算最近一段时间的响应时间分布,并通常以折线图或者柱状图进行呈现。

应对负载

有了描述负载和衡量性能的指标,可以讨论可扩展性了:应对负载增加,如何保持良好性能?

  • 垂直扩展

升级更好的机器

  • 水平扩展

用多个低廉机器分摊负载

负载扩展的两种方式:

  • 自动 如果负载不好预测且多变,则自动较好。坏处在于不易跟踪负载,容易抖动,造成资源浪费。
  • 手动 如果负载容易预测且不长变化,最好手动。设计简单,且不容易出错。

针对不同应用场景:

首先,如果规模很小,尽量还是用性能好一点的机器,可以省去很多麻烦。

其次,可以上云,利用云的可扩展性。甚至如 Snowflake 等基础服务提供商也是 All In 云原生。

最后,实在不行再考虑自行设计可扩展的分布式架构。

两种服务类型:

  • 无状态服务 比较简单,多台机器,外层罩一个 gateway 就行。
  • 有状态服务 根据需求场景,如读写负载、存储量级、数据复杂度、响应时间、访问模式,来进行取舍,设计合乎需求的架构。

可维护性

随着时间的推移,许多新的人员参与到系统开发和运维, 以维护现有功能或适配新场景等,系统都应高效运转。

软件的开发成本不在最初的开发阶段,在于后续的持续投入

  • 修复bug
  • 监控系统正常运行
  • 故障排除
  • 适配新平台
  • 搭配新场景
  • 完善技术缺陷
  • 增加新功能

但是人们只喜欢挖坑,不喜欢填坑。因此可以在设计时,把控好三个原则:

  • 可运维性(Operability)

    便于运维团队无痛接手。

  • 简洁性(Simplicity)

    便于新手开发平滑上手:这需要一个合理的抽象,并尽量消除各种复杂度。如,层次化抽象。

  • 可演化性(Evolvability)

    便于后面需求快速适配:避免耦合过紧,将代码绑定到某种实现上。也称为可扩展性(extensibility)可修改性(modifiability)可塑性(plasticity)

可运维性-方便运维

方便运维团队来保持系统平稳运行。

有效的运维需要这些事情:

  1. 紧盯系统状态,出问题时快速恢复。
  2. 恢复后,复盘问题,定位原因。
  3. 定期对平台、库、组件进行更新升级。
  4. 了解组件间相互关系,避免级联故障。
  5. 建立自动化配置管理、服务管理、更新升级机制。
  6. 执行复杂维护任务,如将存储系统从一个数据中心搬到另外一个数据中心。
  7. 配置变更时,保证系统安全性。

系统具有良好的可维护性,意味着将可定义 的维护过程编写文档和工具以自动化,从而解放出人力关注更高价值事情:

  1. 友好的文档和一致的运维规范。
  2. 细致的监控仪表盘、自检和报警。
  3. 通用的缺省配置。
  4. 出问题时的自愈机制,无法自愈时允许管理员手动介入。
  5. 将维护过程尽可能的自动化。
  6. 避免单点依赖,无论是机器还是人。

简洁性-降低复杂度

复杂度表现:

  1. 状态空间的膨胀。
  2. 组件间的强耦合。
  3. 不一致的术语和命名
  4. 为了提升性能的 hack。
  5. 随处可见的补丁(workaround)。

需求很简单,但不妨碍你实现的很复杂:过多的引入了额外复杂度------非问题本身决定的,而由实现所引入的复杂度。

好的解决方法是抽象

复杂度高通常是问题理解的不够本质,写出了"流水账 "(没有任何抽象,abstraction)式的代码。

如果你为一个问题找到了合适的抽象,那么问题就解决了一半,如:

  1. 高级语言隐藏了机器码、CPU 和系统调用细节。
  2. SQL 隐藏了存储体系、索引结构、查询优化实现细节。

如何找到合适的抽象?

  1. 从计算机领域常见的抽象中找。
  2. 从日常生活中常接触的概念找。

总之,一个合适的抽象,要么是符合直觉 的;要么是和你的读者共享上下文的。

可演化性-快速适配

系统不会一成不变,需求不断变化

  • 适配新的外部环境
  • 新的用例
  • 业务优先级变化
  • 用户所要的新功能
  • 新平台取代旧平台
  • 法律和监管要求的变化
  • 业务增长促使架构演变

应对:

  • 组织流程方面,敏捷开发
  • 设计方面,依赖前两点,合理封装和抽象
相关推荐
JaguarJack10 小时前
PHP 现代特性速查 写出更简洁安全的代码(中篇)
后端·php
Victor35611 小时前
Redis(104)Redis的最大数据量是多少?
后端
Victor35611 小时前
Redis(105)Redis的数据类型支持哪些操作?
后端
鬼火儿18 小时前
SpringBoot】Spring Boot 项目的打包配置
java·后端
cr7xin19 小时前
缓存三大问题及解决方案
redis·后端·缓存
间彧20 小时前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧20 小时前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
间彧20 小时前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧20 小时前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端
间彧20 小时前
如何为Docker Compose中的服务配置健康检查,确保服务真正可用?
后端