目录

系统架构设计师考点分析

目录

占比比较大的从高到低分别是 软件工程基础知识 > 系统架构设计的基础知识 > 系统质量属性与架构评估 > 数据库 > 其他。

选择考前只用复习重点,其他凭经验就差不多了。

  • 瀑布模型
    • 从上一项开发活动接受该活动的工作对象作为输入
    • 实施该项活动完成对应的工作内容
    • 工作成果作为输出
    • 对工作成果进行评审,通过进行下一项,否则返回前一项
  • 原型模型
    • 第一步就是创建一个快速原型,与客户讨论弄清楚当前系统的需求
    • 对用户的需求是动态响应、逐步纳入的
  • 螺旋模型
    • 是一个演化过程模型
    • 将原型模型和瀑布模型结合起来
    • 四个阶段分别为:制定计划、风险分析、实施工程、客户评估
    • 强调了风险分析,适用于庞大复杂的高风险系统
  • V 模型
    • 每个步骤有对应的测试
    • 用于需求明确和需求变更不频繁的情况
  • 增量模型
    • 首先开发核心模块功能,然后与用户确认,每次开发一部分功能,并于用户确认
    • 优先级最高的服务最先交付
    • 不利于模块划分,难点在于如何将客户需求划分为多个增量
    • 与原型模型不同的是增量模型每个增量版本都是独立可操作的作品
  • 敏捷模型
    • 核心思想是适应性、以人为本、迭代增量
  • 统一过程模型 RUP
    • 9 个核型工作流:业务建模、需求、分析与设计、实现、测试、部署、配置与变更管理、项目管理、环境
    • 特点:用例驱动、以体系结构为中心
    • 四个阶段
      • 初始阶段:定义最终产品视图和业务模型,并确定系统范围
      • 细化阶段:设计及确定系统的体系结构,制订工作计划及资源要求
      • 构造阶段:构造产品并继续演进需求、体系结构、计划直到产品提交
      • 移交阶段:把产品提交给用户使用
    • 4+1 视图模型
      • 用例视图,分析人员和测试人员关注
      • 逻辑视图,最终用户关注
      • 实现视图,程序员关注
      • 进程视图,系统集成人员关注
      • 部署视图,系统工程师关注

也称为模块测试,测试的对象是可独立编译或汇编的程序模块、软件构件或 00 软件中的类(统称为模块),测试依据是软件详细设计说明书

目的是检查模块之间,以及模块和已集成的软件之间的接口关系,并验证已集成的软件是否符合设计要求。测试依据是软件概要设计文档

主要用于验证软件的功能、性能和其他特性是否与用户需求一致。根据用户的参与程度,通常包括以下类型:

  • 内部确认测试:主要由软件开发组织内部按照 SRS 进行测试。
  • Alpha 测试:用户在开发环境下进行测试。
  • Beta 测试:用户在实际使用环境下进行测试,通过改测试后,产品才能交付用户。
  • 验收测试:针对 SRS,在交付前以用户为主进行的测试。其测试对象为完整的、 集成的计算机系统。验收测试的目的是,在真实的用户工作环境下,检验软件系统是否满足开发技术合同或 SRS。验收测试的结论是用户确定是否接收该软件的主要依据。除应满足一般测试的准入条件外,在进行验收测试之前,应确认被测软件系统已通过系统测试。

测试对象是完整的、集成的计算机系统;测试的目的是在真实系统工作环境下,验证完成的软件配置项能否和系统正确连接,并满足系统/子系统设计文档和软件开发合同规定的要求。测试依据是用户需求或开发合同

主要内容包括功能测试、健壮性测试、性能测试、用户界面测试、安全性测试、 安装与反安装测试等,其中,最重要的工作是进行功能测试与性能测试。功能测试主要采用黑盒测试方法;性能测试主要指标有响应时间、吞吐量、并发用户数和资源利用率等。

测试对象是软件配置项测试目的是检验软件配置项与 SRS 的一致性。测试的依据是 SRS。在此之间,应确认被测软件配置项已通过单元测试和集成测试。

测试目的是测试软件变更之后,变更部分的正确性和对变更需求的符合性,以及软件原有的、正确的功能、性能和其他规定的要求的不损害性。

  • ⭐ 考察软件过程模型的定义
    alt
  • ⭐ 考察软件过程模型的差异
    alt
  • 考察到了 CMMI 的哪一级的成熟度
    alt
  • 考察软件的生命周期
    alt
  • 考察敏捷方法的特点
    alt
  • 考察敏捷方法核心思想
    alt
  • 考察 RUP 九个核心工作流
    alt
  • 考察 RUP 的特点
    alt
  • 考察软件系统用户文档、系统文档的定义
    alt
  • 考察软件工程过程的定义
    alt
  • 考察软件设计四个活动的定义
    alt
  • 考察逆向工程的四个级别的定义
    alt
  • 考察需求工程的两大过程(需求开发、需求管理)的主要活动
    alt
  • 考察内聚程度分类、耦合程度分类
    alt
  • ⭐ 考察各个测试阶段的测试对象、测试依据、测试目的
    alt
  • 求环形复杂度:看图带公式即可 环形复杂度 = 判定节点数 + 1
  • 考察白盒测试覆盖级别
    alt
  • 考察静态测试、动态测试的定义
    alt
  • 考察构件的定义
  • 求最低成本完成项目需要多少天:注意间接费用,可能赶工成本更低
  • 考察遗留系统的演化策略
    alt
  • 第一范式:关系中的每一个分量必须是一个不可分的数据项
  • 第二范式:如果关系 R 属于 1NF,且每一个非主属性完全函数依赖于任何一个候选码,则 R 属于 2NF。通俗地说,2NF 就是在 1NF 的基础上,表中的每一个非主属性不会依赖复合主键中的某一个列
  • 第三范式:在满足 1NF 的基础上,表中不存在非主属性对码的传递依赖
  • BC 范式,是指在第三范式的基础上进一步消除主属性对于码的部分函数依赖和传递依赖
  • 第四范式: 是限制关系模式的属性间不允许有非平凡且非函数依赖的多值依赖

函数依赖的公理系统

  • ⭐ 求关系模式达到了第几范式:理解各种范式的限定条件。
    alt
  • 考察函数依赖的公理系统: 背下并理解四率两规则及其对应的数学代数表示。
    alt
  • 求等价的关系代数表达式:常见的就是给个自然连接的表达式,等价的是一个笛卡尔积的表达式,笛卡尔积转自然连接需要经过投影和选择。还有能用数字代替列名,从 1 开始,如果表达式中看到带引号的数字可以直接排除
    alt
  • 求关系代数等价的 SQL: 通常就是考察投影和选择,主要是行的问题,把属性列写一下很容易就能答出来。
    alt
  • 求元组个数和属性列数:属性列很简单,自然连接求交集,笛卡尔积求并集,元组个数是,通过属性列相同且值相同连接后剩余的行数。
    alt
  • 求候选键/属性闭包等式成立的代数表达式:根据依赖集找出从未在右边出现过的属性,其必然是候选键之一,然后以其为基础看看能不能遍历所有属性,将无法遍历的加入候选键中。属性闭包表达式括号里所有属性,能求出依赖的所有属性就是闭包等式成立,通常就是全部候选键。
    alt
  • 求模式分解后是否保持函数依赖、是否无损连接:是否保持函数依赖先求分解后的模式分别的函数依赖,如果拆分后的属性,包含了原来的依赖关系中的所有属性,那么就能继承相应的依赖关系。然后如果剩余全部未被包含的依赖能通过函数依赖的公理系统得到,那么就能说保持了函数依赖。是否保持无损连接分解后的模式先求交集,然后看交集的属性能不能推出,任意一个差集里的属性,如果可以那就算无损连接。
    alt
  • 求属于概念结构设计的什么冲突:理解概念结构的冲突。如果连着解决冲突的方式一起考,也可以根据解决方式倒推冲突类型
    alt
  • 求事务能否加锁成功:很简单,排它(写)锁就是一个事务加了,其他事务什么锁也加不了。共享(读)锁就是一个事务加了,其他事务只能加共享(读)锁,不能加排它(写)锁。
    alt
  • 软件架构为软件系统提供了一个结构、行为和属性的高级抽象
  • 软件架构风格是特定应用领域的惯用模式,架构定义一个词汇表和一组约束
  • 数据流:面向数据流,按照一定的顺序从前向后执行程序
    • 批处理:以整体为单位
    • 管道-过滤器:优点是模块复用、模块独立、可维护性和可扩展行较强、具有并发性;缺点是不适用于交互性较强的应用
  • 调用/返回:构件之间存在互相调用的关系,一般是显式调用
    • 主程序/子程序
    • 面向对象:优点是模块化、封装、代码共享性好、易维护、可扩充性好;缺点是增加了对象之间的依赖关系
    • 层次结构:支持系统设计过程中的逐级抽象、可扩展性好、支持软件复用;缺点是不同层次之间耦合度高的系统很难实现
  • 独立构件:构件之间是相互独立的,不存在显式调用,而是通过事件触发,异步执行
    • 进程通信
    • 事件驱动(隐式调用):容易实现并发处理和多任务、可扩展性好、具有类层次结构、简化代码;缺点是因为是树形结构所以削弱了对系统计算的控制能力、各个对象间的关系复杂
  • 虚拟机:自定义了一套规则供提供者使用,使用者基于规则来灵活开发构件
    • 解释器
    • 规则系统:适用场景为 DSS、人工智能、专家系统
  • 仓库:以数据为中心,所有的操作都是围绕数据中心进行的
    • 数据库
    • 超文本
    • 黑板:适用场景为语音识别、知识推理
  • 闭环控制:软件与硬件之间可以粗略的表示为一个反馈循环。适用场景为定速巡航、空调温度调节
  • C2:通过连接件绑定在一起的按照一组规则运作的并行构件网络

软件架构复用的类型包括机会复用和系统复用。机会复用是指开发过程中,只要发现有可复用的资产,就对其进行复用。系统复用是指在开发之前,就要进行规划,以决定哪些需要复用。

复用的基本过程主要包括 3 个阶段:首先构造 / 获取可复用的软件资产,其次管理这些资产(构件库),最后针对特定的需求,从这些资产中选择可复用的部分,以开发满足需求的应用系统。

ABSD 方法是架构驱动,强调由业务、质量和功能需求的组合驱动架构设计。 它强调采用视角和视图来描述软件架构,采用用例和质量属性场景来描述需求。 进一步来说,用例描述的是功能需求,质量属性场景描述的是质量需求(或侧重于非功能需求)。

使用 ABSD 方法,设计活动可以从项目总体功能框架明确就开始,这意味着需求获取和分析还没有完成,就开始了软件设计。

ABSD 方法有三个基础。第一个基础是功能的分解,使用已有的基于模块的内聚和耦合技术;第二个基础是通过选择架构风格来实现质量和业务需求;第三个基础是软件模板的使用,软件模板利用了一些软件系统的结构。

  1. 体系结构需求
  2. 体系结构设计
  3. 体系结构文挡化
  4. 体系结构复审
  5. 体系结构实现
  6. 体系结构演化

DSSA 就是专用于一类特定类型的任务(领域)的、在整个领域中能有效地使用的、为成功构造应用系统限定了标准的组合结构的软件构件的集合

垂直域:在一个特定领域中的通用的软件架构,是一个完整的架构。

水平域:在多个不同的特定领域之间的相同的部分的小工具(如购物和教育都有收费系统,收费系统即是水平域)。

领域分析:这个阶段的主要目标是获得领域模型(领域需求)。识别信息源, 即整个领域工程过程中信息的来源,可能的信息源包括现存系统、技术文献、 问题域和系统开发的专家、用户调查和市场分析、领域演化的历史记录等,在此基础上就可以分析领域中系统的需求,确定哪些需求是领域中的系统广泛共享的,从而建立领域模型

领域设计:这个阶段的目标是获得 DSSA。DSSA描述在领域模型中表示的需求的解决方案,它不是单个系统的表示,而是能够适应领域中多个系统的需求的一个高层次的设计。建立了领域模型之后,就可以派生出满足这些被建模的领域需求 DSSA。

领域实现:这个阶段的主要目标是依据领域模型和 DSSA开发和组织可重用信息。这些可重用信息可能是从现有系统中提取得到,也可能需要通过新的开发得到。

  • ⭐ 给定系统需求问应采用什么架构风格:架构风格分类理解记忆
    alt
  • ⭐ 考察软件架构复用类型的定义
    alt
  • 考察 ABSD 开发过程
  • 考察 DSSA 的定义
  • 考察 DSSA 三个基本活动:目的与活动的对应关系
  • 性能
    • 指系统的响应能力,即要经过多长时间能对某个事件作出响应,或者在某段时间内系统所能处理的事件个数。如响应时间、吞吐量
    • 优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度
  • 可用性
    • 是系统能够正常运行的时间比例,经常用故障间隔时间表示
    • 心跳、Ping/Echo、冗余、选举
  • 可靠性
    • 指软件系统在应用或系统错误面前,在意外或错误使用的情况下维持软件系统功能特性的基本能力。如 MTTF、MTBF、MTTR
    • 心跳、Ping/Echo、冗余、选举
    • 与可用性同时出现时选可用性
  • 安全性
    • 是指系统在向合法用户提供服务的同时能够阻止非授权用户使用的企图或拒绝服务的能力。如保密性、完整性、不可抵赖性、可控性
    • 入侵检测、用户认证、用户授权、追踪审计
  • 可修改性
    • 指能够快速的以较高的性价比对系统进行变更的能力
    • 接口实现分离、抽象、信息隐藏
  • 可变性
    • 指体系结构经扩充或变更而成为新的体系结构的能力
    • 与可修改性同时出现时选可修改性
  • 功能性
    • 指系统所能完成所期望的工作的能力
  • 互操作性
    • 指作为系统组成部分的软件不是独立存在的,经常与其他系统或自身环境相互作用

敏感点:是指为了实现某种特定的质量属性,一个或多个构件所具有的特性。

权衡点:是影响多个质量属性的特性,是多个质量属性的敏感点。

风险点与非风险点不是以标准专业术语形式出现的,只是一个常规概念,即可能引起风险的因素,可称为风险点。某个做法如果有隐患,有可能导致一些问题,则为风险点;而如果某件事是可行的可接受的,则为非风险点

SAAM 是一种非功能质量属性的架构分析方法,是最早形成文档并得到广泛应用的软件架构分析方法。

SAAM 的主要输入是问题描述、需求声明和架构描述

SAAM 分析活动包括 5 个步骤,即场景开发、架构描述、单个场景评估、场景交互和总体评估

架构权衡分析法 ATAM,让架构师明确如何权衡多个质量目标,参与者有评估小组、项目决策者和其他项目相关人。

ATAM 被分为四个主要的活动领域,分别是场景和需求收集、体系结构视图和场景实现、属性模型构造和分析、折中

整个评估过程强调以属性作为架构评估的核心概念。主要针对性能、可用性、安全性和可修改性,在系统开发之前,对这些质量属性进行评价和折中。

  • ⭐ 给定描述问是哪个面向架构的质量属性:理解记忆
    alt
    alt
  • ⭐ 考察敏感点、权衡点、风险点的判断
    alt
    alt
  • 考察质量属性场景的六个部分
  • 考察 SAAM 基于场景的架构分析方法的定义
  • 考察 ATAM 架构权衡分析法的定义
  • 创建型
    • 抽象工厂(Abstract Factory):提供一个接口,可以创建一系列相关或互相依赖的对象,而无需指定它们具体的类
    • 构建器(Builder):将一个复杂类的表示与其构造相分离,使得相同的构建过程能得出不同的表示
    • 工厂方法(Factory Method):定义一个创建对象的接口,但由子类决定需要实例化哪一个类,使得子类实例化过程推迟
    • 原型(Prototype):用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象
    • 单例(Singleton):保证一个类只有一个实例,并且提供一个访问它的全局访问点
  • 结构型
    • 适配器(Adapter):将一个类的接口转换成用户希望得到的另一种接口,它是原本不兼容的接口得以协同工作
    • 桥接(Bridge):将类的抽象部分和它的实现部分分离开来,使得它们可以独立变化
    • 组合(Composite):将对象组合成树形结构以表示“整体-部分”的层次结构,使得用户对单个对象和组合对象的使用具有一致性
    • 装饰(Decorator):动态的给一个对象添加一些额外的职责。提供了用子类扩展功能的一个灵活的替代
    • 外观(Facade):定义一个对外统一的高层接口,为子系统中的一组接口提供一个一致的外观,从而简化了该子系统的使用
    • 享元(Flyweight):提供支持大量细粒度对象共享的有效方法
    • 代理(Proxy):为其他对象提供一种代理控制这个对象的访问
  • 行为型
    • 职责链(Chain of Responsibility):传递请求、职责链接
    • 命令(Command):日志记录、可撤销
    • 解释器(Interpreter):解释器、虚拟机
    • 迭代器(Iterator):顺序访问、不暴露内部
    • 中介者(Mediator):不直接引用
    • 备忘录(Memento):保存、恢复
    • 观察者(Observer):通知、自动更新
    • 状态(State):状态改变行为
    • 策略(Strategy):算法替换
    • 模版方法(Template Method):算法骨架、步骤延迟、子类实现
    • 访问者(Visitor):数据和操作分离

案例历年真题
案例历年真题

  1. 架构: 2023 之前案例的第一题是稳定的考架构风格+质量属性送分,教材改版后不出意外的情况下应该是下篇的八大架构中出题了,所以新版教材中的八大架构的特点和架构图是重中之重。
  2. 建模(需求分析):结构化建模(需求分析)和面向对象的建模(需求分析)轮流来,结构化建模主要考数据流图+数据字典,面向对象的建模主要考用例图、类图、活动图、状态图
  3. 数据库:主要是数据库实际应用中面临的问题,基本和数据库面试题差不多,还比较喜欢考 Redis。
  4. Web:偏理论的居多,主要是 Java 方向,撞到了枪口上再选。
  5. 嵌入式/新技术:尽量不选。

总体来说的选题思路:架构强制选,建模必选,数据库 > WEB,嵌入式/新技术不要选。

2023 年之前第一题是固定的质量属性+架构风格送 25 分。

为了防止第一题才改回原来的题型这里还是要学习一下。

软件架构风格对比
软件架构风格对比

某电子商务公司拟升级其会员与促销管理系统,向用户提供个性化服务,提高用户的粘性。在项目立项之初,公司领导层一致认为本次升级的主要目标是提升会员管理方式的灵活性,由于当前用户规模不大,业务也相对简单,系统性能方面不做过多考虑。新系统除了保持现有的四级固定会员制度外,还需要根据用户的消费金额、偏好、重复性等相关特征动态调整商品的折扣力度,井支持在特定的活动周期内主动筛选与活动主题高度相关的用户集合,提供个性化的打折促销活动。

在需求分析与架构设计阶段,公司提出的需求和质量属性描述如下:

  • (a)管理员能够在页面上灵活设置折扣力度规则和促销活动逻辑,设置后即可生效;
  • (b)系统应该具备完整的安全防护措施,支持对恶意攻击行为进行检测与报警;
  • (c)在正常负载情况下,系统应在 0.3 秒内对用户的界面操作请求进行响应;
  • (d)用户名是系统唯一标识,要求以字母开头,由数字和字母组合而成,长度不少于 6 个字符;
  • (e)在正常负载情况下,用户支付商品费用后在 3 秒内确认订单支付信息;
  • (f)系统主站点电力中断后,应在 5 秒内将请求重定向到备用站点;
  • (g)系统支持横向存储扩展,要求在 2 人天内完成所有的扩展与测试工作;
  • (h)系统宕机后,需要在 10 秒内感知错误,并自动启动热备份系统;
  • (i)系统需要内置接口函数,支持开发团队进行功能调试与系统诊断;
  • (j)系统需要为所有的用户操作行为进行详细记录,便于后期查阅与审计;
  • (k)支持对系统的外观进行调整和配置,调整工作需要在 4 人天内完成。

在对系统需求、质量属性描述和架构特性进行分析的基础上,系统架构师给出了两种候选的架构设计方案, 公司目前正在组织相关专家对系统架构进行评估。

在架构评估过程中,质量属性效用树(utility tree)是对系统质量属性进行识别和优先级排序的重要工具。请将合适的质量属性名称填入图 1-1 中(1)、(2)空白处,并选择题干描述的 (a)~(k)填入(3)~(6) 空白处,完成该系统的效用树。

图 1-1
图 1-1

针对该系统的功能,李工建议采用面向对象的架构风格,将折扣力度计算和用户筛选分别封装为独立对象,通过对象调用实现对应的功能;王工则建议采用解释器 (interpreters)架构风格,将折扣力度计算和用户筛选条件封装为独立的规则,通过解释规则实现对应的功能。请针对系统的主要功能,从折扣规则的可修改性、个性化折扣定义灵活性和系统性能三个方面对这两种架构风格进行比较与分析,并指出该系统更适合采用哪种架构风格。

先补充质量属性名称,考案例的时候基本每年都是性能、安全性、可用性、可修改性这四个,根据叶子节点已有的质量属性描述基本一下就能填出来。

然后使用排除法很快就把质量属性描述给补充完整了。主要会有一些功能描述混淆视线,拿不准的可以先跳过。性能、可用性、可修改性描述中都有时间限定条件,没有的话就是功能描述。

先回答采用那种风格,然后按题目中规定要对比的几个方面分别说明。其实很简单,选风格我估计大家都能选对,题干描述里的倾向性很明显的。

只要选对了风格,题干中需要的方面就是选择的风格占优,不需要的方面就是未被选择的风格占优。

答题格式参考如下:

应该选择解释器架构风格。

折扣规则的可修改性:解释器风格比面向对象方式实现强。因为解释器风格折扣规则是独立的语法规则,由解释器可对变化的规则进行解析,修改更容易。而面向对象相对固定,有变化需要修改具体的类。

个性化折扣定义灵活性:解释器强于面向对象,解释器可以根据用户灵活解释执行规则,做到千人千面。

系统性能:面向对象优于解释器。面向对象的实现相对固定,而解释器是运行期动态绑定执行。

不是很适合出案例题,偏宏观概念的东西比较多,就算考到的话不太把握重点。

某航空公司信息系统架构图
某航空公司信息系统架构图

出案例题的概率很低,考到的话凭经验基本也能拿到不少分。

出案例题的概率中等,还是要记忆一下。

某汽车公司云原生架构图
某汽车公司云原生架构图

某电商业务云原生架构图
某电商业务云原生架构图

  1. 服务化架构模式:典型模式是微服务和小服务模式。通过服务化架构,把代码模块关系和部署关系进行分离,每个接口可以部署不同数量的实例,单独扩缩容,从而使得整体的部署更经济。
  2. Mesh 化架构模式:把中间件框架(如 RPC、缓存、异步消息等)从业务进程中分离,让中间件 SDK 与业务代码进一步解耦,从而使得中间件升级对业务进程没有影响。分离后在业务进程中只保留很“薄” 的 Client 部分。
  3. Serverless 模式:将**“部署”这个动作从运维中“收走”,使开发者不用关心应用运行地点、操作系统、网络配置、CPU 性能等,也就是把应用的整个运行都委托给云**。
  4. 存储计算分离模式:在云环境中,推荐把各类暂态数据(如 session)、结构化和非结构化持久数据都采用云服务来保存,从而实现存储计算分离。
  5. 分布式事务模式:大颗粒度的业务需要访问多个微服务,必然带来分布式事务问题,否则数据就会出现不一致。架构师需要根据不同的场景选择合适的分布式事务模式。
  6. 可观测架构:可观测架构包括 Loggine、Tracing、 Metrics 三个方面,其中 Logging 提供多个级别的详细信息跟踪,由应用开发者主动提供;Tracing 提供一个请求从前端到后端的完整调用链路跟踪, 对于分布式场景尤其有用;Metrics 则提供对系统量化的多维度度量。
  7. 事件驱动架构:本质上是一种应用/组件间的集成架构模式。可用于服务解耦、增强服务韧性、数据变化通知等场景中。

容器作为标准化软件单元,它将应用及其所有依赖项打包,使应用不再受环境限制,在不同计算环境间快速、可靠地运行。

通过容器技术,企业可以充分发挥云计算弹性优势,降低运维成本。

Kubernetes 已经成为容器编排的事实标准,被广泛用于自动部署,扩展和管理容器化应用。Kubernetes 提供了分布式应用管理的核心能力,包括:资源调度、应用部署与管理、自动修复、服务发现与负载均衡、弹性伸缩、声明式 API、可扩展性架构、可移植性。

微服务模式将后端单体应用拆分为松耦合的多个子应用,每个子应用负责一组子功能。这些子应用称为“微服务”,多个“微服务”共同形成了一个物理独立但逻辑完整的分布式微服务体系。这些微服务相对独立,通过解耦研发、测试与部署流程,提高整体迭代效率。

微服务设计约束:

  1. 微服务个体约束:功能在业务域划分上应是相互独立的,低耦合、单一职责。
  2. 微服务与微服务之间的横向关系:主要从微服务的可发现性和可交互性处理服务间的横向关系, 一般需要服务注册中心。
  3. 微服务与数据层之间的纵向约束:在微服务领域,提供数据存储隔离原则,即数据是微服务的私有资产,对于该数据的访问都必须通过当前微服务提供的 AP1 来访问。
  4. 全局视角下的微服务分布式约束:故障发现时效性和根因精确性始终是开发运维人员的核心诉求。

出案例题的概率低,比较老得架构,这两年新版教材出来,应该都会侧重于新架构。

业务集成参考架构
业务集成参考架构

之前说的案例的选题思路,不要选嵌入式,但是其实第一题必选题最近几次考试有极大的概率考一次鸿蒙系统的架构,在考过之前是重中之重。

鸿蒙 (Harmonyos)整体采用分层的层次化设计,从下向上依次为:内核层、系统服务层、框架层和应用层

系统功能按照系统一子系统一功能/模块逐级展开,在多设备部署场景下, 支持根据实际需求裁剪某些非必要的子系统或功能/模块,

鸿蒙架构图
鸿蒙架构图

  • 内核层:主要由内核子系统和驱动子系统组成。
    • 内核子系统:HarmonyOS 采用多内核设计,支持针对不同资源受限设备选用适合的 OS 内核内核抽象层通过屏蔽多内核差异,对上层提供基础的内核能力
    • 驱动子系统:提供统一外设访问能力和驱动开发、管理框架。
  • 系统服务层:是 HarmonyOS 的核心能力集合,通过框架层对应用程序提供服务
    • 系统基本能力子系统集:为分布式应用在 HarmonyOS 多设备上的运行、调度、迁移等操作提供了基础能力。
    • 基础软件服务子系统集:为 HarmonyOS 提供公共的、通用的软件服务。
    • 增强软件服务子系统集:为 HarmonyOS 提供针对不同设备的、差异化的能力增强型软件服务。
    • 硬件服务子系统集:为 HarmonyOS 提供硬件服务。
  • 应用框架层:为 HarmonyOS 的应用程序提供了 Java/C/C++/JS 等多语言的用户程序框架和 Ability 框架, 以及各种软硬件服务对外开放的多语言框架 API;同时为采用 HarmonyOS 的设备提供了 C/C++/JS 等多语言的框架 API,不同设备支持的 API 与系统的组件化裁剪程度相关。
  • 应用层:包括系统应用和第三方非系统应用。HarmonyOS 的应用由一个或多个 FA (Feature Ability) 或 PA (Particle Ability)组成。其中,FA 有 UI 界面,提供与用户交互的能力; 而 PA 无 UI 界面,提供后台运行任务的能力以及统一的数据访问抽象。
  1. 分布式架构首次用于终端 OS,实现跨终端无缝协同体验。
  2. 确定时延引擎和高性能 IPC 技术实现系统天生流畅。
  3. 基于微内核架构重塑终端设备可信安全。
  4. 通过统一 IDE 支撑一次开发,多端部署,实现跨终端生态共享。

HarmonyOS 架构的系统安全性主要体现在搭载 HarmonyOS 的分布式终端上,可以保证“正确的人,通过正确的设备,正确地使用数据〞。这里通过“分布式多端协同身份认证”来保证“正确的人”,通过“在分布式终端上构筑可信运行环境”来保证“正确的设备〞,通过“分布式数据在跨终端流动的过程中,对数据进行分类分级管理〞来保证“正确地使用数据”。

  1. 分布式软总线是多种终端没备的统一基座,为设备之间的互联互通提供了统一的分布式通信能力;
  2. 分布式设备虚拟化平台可以实现不同设备的资源融合、设备管理、数据处理,多种设备共同形成一个超级虚拟终端。针对不同类型的任务,为用户匹配并选择能力合适的执行硬件
  3. 分布式数据管理基于分布式软总线的能力,实现应用程序数据和用户数据的分布式管理。用户数据不再与单一物理设备绑定,业务逻辑与数据存储分离,应用跨设备运行时数据无缝衔接;
  4. 分布式任务调度构建统一的分布式服务管理(发现、同步、注册、调用)机制,支持对跨设备的应用进行远程启动、远程调用、远程连接以及迁移等操作,选择合适的设备运行分布式任务。

基本不能可能在架构里考到。

出案例题的概率低,这两年新版教材出来,应该都会侧重于新架构。

出案例题的概率很高,但是 2023 年 11 月考过了,至少会隔一次再出。

某网奥运 Lambda 架构图
某网奥运 Lambda 架构图

  • 结构化特点:自顶向下,逐步分解,面向数据
  • 三大模型:功能模型(数据流图)、行为模型(状态转换图)、数据模型(E-R 图)以及数据字典。
  • 数据字典:数据字典是在 DFD 的基础上,对 DFD 中出现的所有命名元素都加以定义,使得每个图形元素的名字都有一个确切的解释。DFD 和数据字典等工具相配合,就可以从图形和文字两个方面对系统的逻辑模型进行完整的描述。是所有人员工作的依据,统一的标准。它可以确保数据在系统中的完整性和一致性。数据字典中一般有 6 类条目,分别是数据元素、数据结构、数据流、数据存储、加工逻辑和外部实体。不同类型的条目有不同的属性需要描述。

结构化建模

  • 作用:分析阶段,建立系统的功能模型,从而完成需求分析。设计阶段,为模块划分与模块之间接口设计提供依据。
  • 数据流:由一组固定成分的数据组成,表示数据的流向。在 DFD 中,数据流的流向必须经过加工。
  • 加工:描述了输入数据流到输出数据流之间的变换。
    • 有输入但是没有输出,称之为“黑洞”。
    • 有输出但没有输入。称之为“奇迹”。
    • 输入不足以产生输出,我们称之为“灰洞”。
  • 数据存储:用来存储数据。
  • 外部实体(外部主体):是指存在于软件系统之外的人员或组织,它指出系统所需数据的发源地(源)和系统所产生的数据的归宿地(宿)。
  • 数据流图的平衡原则
    • 父图(上层数据流图)与子图(下层数据流图)平衡:
      • 个数一致:两层数据流图中的数据流个数一致
      • 方向一致:两层数据流图中的数据流方向一致
    • 子图内部的平衡
      • 黑洞:加工只有输入没有输出
      • 奇迹:加工只有输出没有输入
      • 灰洞:加工中输入不足以产生输出

数据流图
数据流图
分层数据流图
分层数据流图

  • 作用
    • 数据建模与抽象:E-R 图帮助数据库设计师从现实世界中抽象出数据实体和它们之间的关系,将复杂的现实情况转化为易于理解和表达的图形化结构。通过 E-R 图,设计师可以更好地捕捉数据模型的本质,从而准确地定义数据结构。
    • 数据可视化:E-R 图可视化了数据实体、属性和关系之间的联系,使得数据库设计更加直观和易于沟通。设计师和利益相关者可以通过 E-R 图更容易地理解数据库的结构和组织,从而减少沟通误差。
    • 数据规范化:E-R 图可以帮助设计师进行数据规范化,将数据库设计转化为满足数据库范式要求的合理结构。数据规范化是数据库设计的重要原则,有助于减少数据冗余和提高数据的一致性。
    • 数据库系统设计指导:E-R 图是数据库系统设计的蓝图,为开发人员提供了关于数据库结构和关系的指导,从而帮助他们更好地实现数据库系统。
  • 用 E-R 图来描述概念数据模型,世界是由一组称作实体的基本对象和这些对象之间的联系构成的。
  • 在 E-R 模型中,使用椭圆表示属性(一般没有)、长方形表示实体、菱形表示联系,联系的两端要填写联系类型。
  • E-R 模型转关系模型,每个实体都对应一个关系模式;联系分为三种:
    • 1:1 联系中,联系可以放到任意的两端实体中,作为一个属性(要保证 1:1 的两端关联),也可以转换为一个单独的关系模式。
    • 1:N 的联系中,联系可以单独作为一个关系模式,也可以在 N 端中加入 1 端实体的主键。
    • M:N 的联系中,联系必须作为一个单独的关系模式,其主键是 M 和 N 端的联合主键。

E-R 图
E-R 图
E-R 图
E-R 图

状态转换图
状态转换图

状态转换图图例
状态转换图图例

只能有一个初始状态,可以有多个中间状态和最终状态。中间状态的状态名必须要有,状态变量和行为可选。

面向对象的分析,是为了确定问题域,理解问题。包含五个活动:认定对象、组织对象、描述对象间的相互作用、确定对象的操作、定义对象的内部信息

面向对象需求建模
面向对象需求建模

面向对象的分析模型主要由顶层架构图、用例与用例图、领域概念模型构成;

面向对象的设计模型则包含以包图表示的软件体系结构图、以交互图表示的用例实现图、完整精确的类图、针对复杂对象的状态图和用以描述流程化处理过程的活动图等。

面向对象的分析模型和设计模型
面向对象的分析模型和设计模型

UML(统一建模语言):是一种可视化的建模语言,而非程序设计语言,支持从需求分析开始的软件开发的全过程。 从总体上来看,UML 的结构包括构造块、规则和公共机制三个部分。

  1. 构造块。UML 有三种基本的构造块,分别是事物(thing)、关系 (relationship)和图(diagram)。事物是 UML 的重要组成部分,关系把事物紧密联系在一起,图是多个相互关联的事物的集合。
  2. 公共机制。公共机制是指达到特定目标的公共 UML 方法。
  3. 规则。规则是构造块如何放在一起的规定。

UML 的关系:

  1. 依赖:一个事物的语义依赖于另一个事物的语义的变化而变化
  2. 关联:是一种结构关系,描述了一组链,链是对象之间的连接。分为组合和聚合,都是部分和整体的关系,其中组合事物之间关系更强。两个类之间的关联,实际上是两个类所扮演角色的关联,因此,两个类之间可以有多个由不同角色标识的关联。
  3. 泛化:一般/特殊的关系,子类和父类之间的关系
  4. 实现:一个类元指定了另一个类元保证执行的契约。

UML 的关系
UML 的关系

UML 图
UML 图

静态图,展现了一组用例、参与者以及它们之间的关系。用例图中的参与者是人、硬件或其他系统可以扮演的角色;用例是参与者完成的一系列操作,用例之间的关系有扩展、包含、泛化

用例图
用例图

静态图,为系统的静态设计视图,展现一组对象、接口、协作和它们之间的关系

类图
类图

动态图,展现了一个状态机,描述单个对象在多个用例中的行为,包括简单状态和组合状态。转换可以通过事件触发器触发,事件触发后相应的监护条件会进行检查。状态图中转换和状态是两个独立的概念,如下:图中方框代表状态,箭头上的代表触发事件,实心圆点为起点和终点。

状态图
状态图

动态图,是一种特殊的状态图,展现了在系统内从一个活动到另一个活动的流程。活动的分岔和汇合线是一条水平粗线。牢记下图中并发分岔、并发汇合、监护表达式、分支、流等名词及含义。每个分岔的分支数代表了可同时运行的线程数。活动图中能够并行执行的是在一个分岔粗线下的分支上的活动。

活动图
活动图

静态图,展现某一时刻一组对象及它们之间的关系,为类图的某一快照。在没有类图的前提下,对象图就是静态设计视图。

对象图
对象图

动态图,是场景的图形化表示,描述了以时间顺序组织的对象之间的交互活动。有同步消息(进行阻塞调用,调用者中止执行,等待控制权返回,需要等待返回消息,用实心三角箭头表示),异步消息(发出消息后继续执行,不引起调用者阳塞,也不等待返回消息,由空心箭头表示)、返回消息(由从右到左的虚线箭头表示)三种。

序列图
序列图

动态图,即协作图,强调参加交互的对象的组织

通信图
通信图

静态图,为系统静态实现视图,展现了一组构件之间的组织和依赖

构件图
构件图

静态图,为系统静态部署视图,部署图物理模块的节点分布。它与构件图相关,通常一个结点包含一个或多个构件。其依赖关系类似于包依赖,因此部署组件之间的依赖是单向的类似于包含关系。

部署图
部署图

SysML 是一种通用图形建模语言,用于指定,分析,设计和验证可能包括硬件,软件,信息,人员, 程序和设施的复杂系统。特别是,该语言提供了图形表示,其具有用于建模系统需求,行为,结构和参数的语义基础,用于与其他工程分析模型集成。

  • SysML 比 UML 更好地表达系统工程语义(符号解释)。它减少了 UML 的软件偏差,并为需求管理和性能分析添加了两种新的图表类型:需求图和参数图。
  • SysML 比 UML 更小,更容易学习。由于 SysML 删除了许多以软件为中心和无偿的构造,因此在图表类型(9 对 13)和总结构中测量的整体语言较小。
  • SySML 和 UML 间存在交集,即 SysML 语言中的部分图是和 UML 中的相应图是一致的,例如用例图。同时,SysML 也有基于 UML 扩展而来的图,例如活动图。另外,还有一部分图是 SysML 所特有的,这些图与 UML 间没有关系,例如需求图。

SysML 提供了需求图 (Requirements Diagram)用于对系统需求,以及需求与需求及其他元素的追湖关系进行建模,而在 UML 中则没有需求图。

用例可以有效地捕获功能需求,但不适合表达非功能需求。将基于文本的需求合并到 SysML 中可有效适应各种需求。

需求图
需求图

SysML 规定了七个需求关系,使建模者能够将需求彼此关联以及与其他模型元素关联。这些关系包括定义需求层次结构、派生需求、满足需求、验证需求和细化需求的关系。然而,这些关系的语义并不是在形式上定义的,而是可以解释的。因此,有必要定义一些关于如何使用这些关系的启发式、指南和实践,以便有一个一致的模型。

  1. 复合关系:复合需求可以包含需求层次结构中的子需求。复合需求可以声明系统应执行 A 和 B,可以将其分解为系统应执行 A 和系统应进行 B 的子需求。
    复合关系
  2. 派生关系:派生的需求通常对应于系统层次结构下一级的需求。一个简单的例子是车辆加速需求,该需求被分析以导出发动机动力等方面的需求。
    派生关系
  3. 细化关系:细化关系可用于描述如何使用模型元素或元素集进一步细化需求。例如,可以使用用例或活动图来细化基于文本的功能需求。或者,可以使用它来显示基于文本的需求如何细化模型元素。在这种情况下,可以使用一些阐述的文本来细化不太精细的模型元素。
    细化关系
  4. 满足关系:满足关系描述了设计或实现模型如何满足一个或多个需求。然后,系统建模者可以指定旨在满足要求的系统设计元素。
    满足关系
  5. 验证关系:验证关系定义了测试用例或其他模型元素如何验证需求。在 SysML 中,测试用例或其他元素可以用作表示任何标准检验方法的通用机制,分析,演示或测试。
    验证关系
  6. 复制关系:真正需要跨产品系列和项目重用需求。典型的方案是适用于产品和/或产品系列中重复使用的项目和要求的法定法规或合同要求。SysML 引入了从属需求的概念。
    复制关系
  7. 追溯关系:追溯关系提供了需求和任何其他模型元素之间的通用关系。追溯的语义不包含任何实际约束,因此非常弱。但是,追溯关系对于将需求与源文档相关联或在规范树中的规范之间建立关系可能很有用。
    追溯关系

非关系型数据库的优势:

  • 性能:NOSQL 是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过 SQL 层的解析,所以性能非常高。
  • 可扩展性:同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。

关系型数据库的优势:

  • 复杂查询:可以用 SQL 语句方便的在一个表以及多个表之间做非常复杂的数据查询。
  • 事务支持:使得对于安全性能很高的数据访问要求得以实现。

其他:

  • NOSQL 数据库慢慢开始具备 SQL 数据库的一些复杂查询功能,比如 MongoDB。
  • 对于事务的支持也可以用一些系统级的原子操作来实现例如乐观锁之类的方法来曲线救国。

关系数据库与非关系数据库对比
关系数据库与非关系数据库对比

规范化设计后,数据库设计者希望牺牲部分规范化来提高性能

采用反规范化技术的益处:降低连接操作的需求、降低外码和索引的数目,还可能减少表的数目,能够提高查询效率

可能带来的问题:数据的重复存储,浪费了磁盘空间;可能出现数据的完整性问题,为了保障数据的一致性,增加了数据维护的复杂性,会降低修改速度

具体方法:

  1. 增加冗余列:在多个表中保留相同的列,通过增加数据元余减少或避免查询时的连接操作。
  2. 增加派生列:在表中增加可以由本表或其它表中数据计算生成的列,减少查询时的连接操作并避免计算或使用集合函数。
  3. 重新组表:如果许多用户需要查看两个表连接出来的结果数据,则把这两个表重新组成一个表来减少连接而提高性能。
  4. 水平分割表:根据一列或多列数据的值,把数据放到多个独立的表中,主要用于表数据规模很大、表中数据相对独立或数据需要存放到多个介质上时使用。
  5. 垂直分割表:对表进行分割,将主键与部分列放到一个表中,主键与其它列放到另一个表中,在查询时减少 I/O 次数。

一主多从,读写分离,主动同步,是一种常见的数据库架构,一般来说:

  • 主库,提供数据库写服务
  • 从库,提供数据库读服务
  • 主从之间,通过某种机制同步数据

大部分互联网业务读多写少,数据库的读往往最先成为性能瓶颈,如果希望:

  • 线性提升数据库读性能
  • 通过消除读写锁冲突提升数据库写性能

其实如果面临的是“读性能瓶颈”问题,增加缓存可能来得更直接,更容易一点。

  1. 基于程序代码内部实现:在代码中根据 select、insert 进行路由分类,这类方法也是目前生产环境应用最广泛的。 优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。 但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的 Java 应用,如果在程序代码中实现读写分离对代码改动就较大。
  2. 基于中间代理层实现:代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库。好处是源程序不需要做任何改动就可以实现读写分离,坏处是由于多了一层中间件做中转代理,性能上会有所下降,数据访问代理也容易成为性能瓶颈。
  1. 当 Master 节点进行 insert、update、delete 操作时,会按顺序写入到 binlog 中。
  2. salve 从库连接 master 主库,Master 有多少个 slave 就会创建多少个 binlog dump 线程。
  3. 当 Master 节点的 binlog 发生变化时,binlog dump 线程会通知所有的 salve 节点,并将相应的 binlog 内容推送给 slave 节点。
  4. I/O 线程接收到 binlog 内容后,将内容写入到本地的 relay-log。
  5. SQL 线程读取 I/O 线程写入的 relay-log,并且根据 relay-log 的内容对从数据库做对应的操作。

Mysql 主从复制
Mysql 主从复制

这个其实没啥好说的,最常规的 set/get 操作,value 可以是 String 也可以是数字。一般做一些复杂的计数功能的缓存。

这里 value 存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以 cookieId 作为 key,设置 30 分钟为缓存过期时间,能很好的模拟出类似 session 的效果。

使用 List 的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用 lrange 命令,做基于 redis 的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行情信息。就也是个生产者和消费者的场景。LIST 可以很好的完成排队,先进先出的原则。

因为 set 堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用 JVM 自带的 Set 进行去重?因为我们的系统一般都是集群部署,使用 JVM 自带的 Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。

另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

sorted set 多了一个权重参数 score,集合中的元素能够按 score 进行排列。可以做排行榜应用,取 TOP N 操作。

缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库 CPU 和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

解决办法:

  • 大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。
  • 还有一个简单方案就时讲缓存失效时间分散开。
  • 当然也可以考虑,Redis 的高可用模式,哨兵或者是主从。

布隆过滤器是一种概率型数据结构,它的特点是高效的插入和查询,能确定某个字符串一定不存在或者可能存在

布隆过滤器不存储具体数据,所以占用空间小,查询结果存在误差,但误差可控,同时不支持删除操作

布隆过滤器的原理本质上和散列表是一样的。但布隆过滤器为了节约内存,不是使用的数组,而是使用的位图。

当一个元素加入位图时,通过 k 个 hash 函数将元素映射到位图的 k 个点,并把它们置 1;当检索时,再通过 k 个 hash 函数运算检查位图的 k 个点是否都为 1;如果有不为 1 的点,那么认为该 key 不存在;如果全部为 1,则可能存在(跟 hash 函数的个数和 hash 函数的设计有关)。

我们经常会把一部分数据放在 Redis 等缓存,比如产品详情。这样有查询请求进来,我们可以根据产品 Id 直接去缓存中取数据,而不用读取数据库,这是提升性能最简单,最普遍,也是最有效的做法。一般的查询请求流程是这样的:先查缓存,有缓存的话直接返回,如果缓存中没有,再去数据库查询,然后再把数据库取出来的数据放入缓存,一切看起来很美好。但是如果现在有大量请求进来,而且都在请求一个不存在的产品 Id,会发生什么?既然产品 Id 都不存在,那么肯定没有缓存,没有缓存,那么大量的请求都怼到数据库,数据库的压力一下子就上来了,还有可能把数据库打死。这也是黑客利用漏洞攻击的一种方式,大量重复请求不存在的数据

解决方案:

  1. 在 redis 设置<key,null>键值对,依次避免访问数据库;缺点是<key,null>过多会占用过多内存,可以给 key 设置过期 expire key 600ms,停止攻击后最终由 redis 自动清除无用的 key。
  2. 在服务端存储一个布隆过滤器,将存在的 key 放入布隆过滤器中,布隆过滤器可以过滤一定不存在的数据,能够穿透缓存的漏网之鱼,无关紧要。

缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

不同场景下的解决方式可如下:

  • 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
  • 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
  • 若缓存的数据更新频繁或者缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动的重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。
  1. 解决数据容量限制:单个 Redis 实例的数据存储是有限的,当数据量超过单个实例的存储能力时,就需要进行分片。分片可以将数据均匀地分布在多个实例中,从而解决了数据容量限制的问题。
  2. 提高读写性能:分片可以将负载分散到多个 Redis 实例上,同时充分利用多核处理器的性能,提高读写性能。当多个实例并行处理请求时,整体的处理能力将大大增强。
  3. 实现高可用性:通过复制和故障转移机制,使得分片的 Redis 集群可以提供高可用性。当一个节点发生故障时,系统可以自动将请求路由到其他可用的节点,从而实现故障恢复和高可用性。
  4. 支持横向扩展:通过增加新的 Redis 实例,可以方便地实现系统的横向扩展。当业务需求增加时,可以简单地增加更多的节点,而不需要修改现有的代码和架构。

分片需要将不同 key 映射到不同 Redis 实例上存储,所以 key 的映射规则需要制定一个算法,最简单的一个分片方案应该是范围分片。

范围分片理解起来很简单,比如我们存储用户基本信息,我们制定一个算法将用户 user_id 从 0 到 1000 映射到实例 A,user_id 从 1000 到 2000 映射到实例 B,以此类推。这个方案很轻松可以使用。

但是引发了一个问题:我们需要维护 user_id 范围和映射实例之间的关系。而正是这个问题导致范围分片虽然简单,但是效率比其他分片方案低效许多,所以 Redis 中一般不会使用范围分片作为分片方案。

比如我们目前有四个 Redis 实例,我们需要存储一个 key。我们可以通过哈希函数 crc32 将 key 名转换成一个长整型数字,然后对长整型数字对 4 取余,就可以得到映射的实例。

但是这种分配方案一样存在弊端:当我们需要增加或移除 Redis 实例时,就会造成大量 key 无法被命中。

用一致性 Hash 算法可以很好地解决增加和删减节点时,命中率下降的问题。在这个算法中,我们将整个 Hash 值空间组织成一个虚拟的圆环,然后将缓存节点的 IP 地址或者主机名做 Hash 取值后,放置在这个圆环上。当我们需要确定某一个 Key 需要存取到哪个节点上的时候,先对这个 Key 做同样的 Hash 取值,确定在环上的位置,然后按照顺时针方向在环上“行走”,遇到的第一个缓存节点就是要访问的节点。

比方说下面这张图里面,Key 1 和 Key 2 会落入到 Node 1 中,Key 3、Key 4 会落入到 Node 2 中,Key 5 落入到 Node 3 中,Key 6 落入到 Node 4 中。

一致性哈希分片
一致性哈希分片

Redis Cluster 并没有使用一致性 hash,而是引入了哈希槽的概念。

Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽。集群的每个节点负责一部分 hash 槽,举个例子,比如当前集群有 3 个节点,那么节点 A 包含 0 到 5500 号哈希槽,节点 B 包含 5501 到 11000 号哈希槽,节点 C 包含 11001 到 16384 号哈希槽。

  1. 一致性哈希的节点分布基于圆环,无法很好的手动设置数据分布,比如有些节点的硬件差,希望少存一点数据,这种很难操作。而哈希槽可以很灵活的配置每个节点占用哈希槽的数量。
  2. 一致性哈希的某个节点宕机或者掉线后,当该机器上原本缓存的数据被请求时,会从数据源重新获取数据,并将数据添加到失效机器后面的机器,这个过程被称为 “缓存抖动” ,而使用哈希槽的节点宕机,会导致一定范围内的槽不可用,只能通过主从复制加哨兵模式保证高可用。
  3. 基于一致性哈希的特点,当某台机器宕机时,极易引起雪崩,删除节点就会把当前节点所有数据加到它的下一个节点上。这样会导致下一个节点使用率暴增,可能会导致挂掉,如果下一个节点挂掉,下下个节点将会承受更大的压力,最终导致集群雪崩。
  4. Redis Cluster 的槽位空间是可以用户手动自定义分配的,类似于 windows 盘分区的概念,可以手动控制大小。
  5. 相对于哈希槽,一致性哈希算法更复杂。

Redis 的持久化策略有两种:

  • RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存,保存策略。
    • RDB 的优点是:这种文件非常适合用于备份:比如,你可以在最近的 24 小时内,每小时备份一次,并且在每个月的每一天也备份一个 RDB 文件。
    • RDB 的缺点是:如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不合适你。
  • AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里,命令的集合。Redis 默认是快照 RDB 的持久化方式。
    • AOF 的默认策略是每秒钟 Fsync 一次,在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据。
    • 缺点是对于相同的数据集来说,AOF 的文件体积通常要大于 RDB 文件的体积。根据所使用的 Fsync 策略,AOF 的速度可能会慢于 RDB。将 Redis 执行的每一条命令追加到磁盘中,处理巨大的写入会降低 Redis 的性能。

支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。

主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。

前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

主从模式
主从模式

主从模式的优点:

  • 一旦 主节点宕机,从节点 作为 主节点 的 备份 可以随时顶上来。
  • 扩展 主节点 的 读能力,分担主节点读压力。
  • 高可用基石:除了上述作用以外,主从复制还是哨兵模式和集群模式能够实施的基础,因此说主从复制是 Redis 高可用的基石。

也有相应的缺点:

  • 一旦 主节点宕机,从节点 晋升成 主节点,同时需要修改 应用方 的 主节点地址,还需要命令所有 从节点 去 复制 新的主节点,整个过程需要 人工干预。
  • 主节点 的 写能力 受到 单机的限制。
  • 主节点 的 存储能力 受到 单机的限制。
  • 有数据冗余问题。

在复制的基础上,哨兵实现了自动化的故障恢复。

哨兵模式
哨兵模式

  1. 每个 Sentinel 以 每秒钟 一次的频率,向它所有的 主服务器、从服务器 以及其他 Sentinel 实例 发送一个 PING 命令。
  2. 如果一个 实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 所指定的值,那么这个实例会被 Sentinel 标记为 主观下线。
  3. 如果一个 主服务器 被标记为 主观下线,那么正在 监视 这个 主服务器 的所有 Sentinel 节点,要以 每秒一次 的频率确认 该主服务器是否的确进入了 主观下线 状态。
  4. 如果一个 主服务器 被标记为 主观下线,并且有 足够数量 的 Sentinel(至少要达到配置文件指定的数量)在指定的 时间范围 内同意这一判断,那么这个该主服务器被标记为 客观下线。
  5. 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率,向它已知的所有 主服务器 和 从服务器 发送 INFO 命令。
  6. 当一个 主服务器 被 Sentinel 标记为 客观下线 时,Sentinel 向 下线主服务器 的所有 从服务器 发送 INFO 命令的频率,会从 10 秒一次改为 每秒一次。
  7. Sentinel 和其他 Sentinel 协商 主节点 的状态,如果 主节点处于 SDOWN`状态,则投票自动选出新的主节点。将剩余的 从节点 指向 新的主节点 进行 数据复制。
  8. 当没有足够数量的 Sentinel 同意 主服务器 下线时, 主服务器 的 客观下线状态 就会被移除。当 主服务器 重新向 Sentinel 的 PING 命令返回 有效回复 时,主服务器 的 主观下线状态 就会被移除。

哨兵模式原理
哨兵模式原理

优点:

  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  • 主从可以自动切换,系统更健壮,可用性更高。
  • Sentinel 会不断的检查 主服务器 和 从服务器 是否正常运行。当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本向管理员或者其他的应用程序发送通知。

缺点:Redis 较难支持在线扩容,对于集群,容量达到上限时在线扩容会变得很复杂。

一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。

  • 强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大
  • 弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态
  • 最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型

最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。它的提出是为了尽可能地解决缓存与数据库的数据不一致问题。

读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。

Cache-Aside 读流程
Cache-Aside 读流程

更新的时候,先更新数据库,然后再删除缓存

Cache-Aside 写流程
Cache-Aside 写流程

Read/Write Through 模式中,服务端把缓存作为主要数据存储。应用程序跟数据库缓存交互,都是通过抽象缓存层完成的。

从缓存读取数据,读到直接返回,如果读取不到的话,从数据库加载,写入缓存后,再返回响应。看上去和 Cache-Aside 一样,其实 Read-Through 就是多了一层 Cache-Provider。

Read Through 流程
Read Through 流程

写流程也是由缓存抽象层完成数据源和缓存数据的更新。

Write Through 流程
Write Through 流程

Write behind 跟 Read-Through/Write-Through 有相似的地方,都是由 Cache Provider 来负责缓存和数据库的读写。

区别是,Read/Write Through 是同步更新缓存和数据的,Write Behind 则是只更新缓存,不直接更新数据库,通过批量异步的方式来更新数据库。

Write behind 流程
Write behind 流程

这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用。但是它适合频繁写的场景,MySQL 的 InnoDB Buffer Pool 机制就使用到这种模式。

原因很简单,很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。

比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据并进行运算,才能计算出缓存最新的值的。

另外更新缓存的代价有时候是很高的。是不是说,每次修改数据库的时候,都一定要将其对应的缓存更新一份?也许有的场景是这样,但是对于比较复杂的缓存数据计算的场景,就不是这样了。如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新。但是问题在于,这个缓存到底会不会被频繁访问到?

举个栗子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低,用到缓存才去算缓存。

其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。像 mybatis,hibernate,都有懒加载思想。查询一个部门,部门带了一个员工的 list,没有必要说每次查询部门,都里面的 1000 个员工的数据也同时查出来啊。80% 的情况,查这个部门,就只是要访问这个部门的信息就可以了。先查部门,同时要访问里面的员工,那么这个时候只有在你要访问里面的员工的时候,才会去数据库里面查询 1000 个员工。

在写入请求的时候,为什么是先操作数据库呢?为什么不先操作缓存呢?

假设有 A、B 两个请求,请求 A 做更新操作,请求 B 做查询读取操作。

并发读写流程
并发读写流程

  1. 线程 A 发起一个写操作,第一步 del cache
  2. 此时线程 B 发起一个读操作,cache miss
  3. 线程 B 继续读 DB,读出来一个老数据
  4. 然后线程 B 把老数据设置入 cache
  5. 线程 A 写入 DB 最新的数据

这样就有问题啦,缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据。因此,Cache-Aside 缓存模式,选择了先操作数据库而不是先操作缓存。

  1. 先删除缓存
  2. 再更新数据库
  3. 休眠一会(休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒,为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据),再次删除缓存。

延时双删
延时双删

不管是延时双删还是 Cache-Aside 的先操作数据库再删除缓存,如果第二步的删除缓存失败呢,删除失败会导致脏数据。所以可以引入删除缓存重试机制。

  1. 写请求更新数据库
  2. 缓存因为某些原因,删除失败
  3. 把删除失败的 key 放到消息队列
  4. 消费消息队列的消息,获取要删除的 key
  5. 重试删除缓存操作

删除缓存重试机制
删除缓存重试机制

重试删除缓存机制还可以,就是会造成好多业务代码入侵。其实,还可以通过数据库的 binlog 来异步淘汰 key。

以 mysql 为例 可以使用阿里的 canal 将 binlog 日志采集发送到 MQ 队列里面,然后通过 ACK 机制确认处理这条更新消息,删除缓存,保证数据缓存一致性。

通过 binlog 异步删除缓存
通过 binlog 异步删除缓存

论文历年真题
论文历年真题

2020 开始就有关于新版教材下篇八大架构的题目了,当时是超纲的(微服务架构、云原生架构、安全架构)。

后面论文题目架构方面的应该从八大架构里出,排除掉信息系统架构、嵌入式架构、通信系统架构和最近已经考过的,大数据架构最近几次必考需要准备一下。

层次式架构因为考试的人比较多,估计不会考,太简单刷不掉人。

从 2023 的考试题目看,之后的题型应该偏向于实际应用。之前都是论…

应用的话没法准备,就算撞到枪口上也不一定能凑出一篇论文的篇幅,最好还是选教材范围内的,当然选教材范围内的就拿不到高分了,以过为目的。

2023 没考架构方面的论文,考了系统分析中的面向对象分析,要是再考到系统分析的话应该是结构化分析。2023 考了软件可靠性中的可靠性分析与评价方法,最近几次应该考不到软件可靠性章节了。

这次感觉除了架构方面,还比较有可能的就是系统质量属性与架构评估里挑个分析方法考了,架构权衡分析法 ATAM准备一下。

  • 摘要
  • 项目背景
  • 项目启动时间、岗位、职责、投入、建设周期、项目主要建设内容、技术架构
  • 按题目,回应问题,分层结构
  • 开发时间、投入使用后的作用、反馈良好、系统建设困难克服困难、不懈努力后顺利交付
  • ⭐ 大数据架构
    • 数据采集层
      • Kafka:高吞吐量处理大数据流、持久性、容错、社区支持
      • 数据标准化
      • 历史数据重放
    • 数据处理层
      • Flink:实时处理、事件处理、状态管理、精确一次处理确保数据处理不重复
      • 举例子
    • 数据存储层
      • Hadoop:分布式海量数据存储、高容错、可伸缩、安全、数据压缩、通过计算和分布式实现容错成本较低
      • Redis:高可用、简单
    • 数据展现层和安全监控层
      • Vue:组件化、轻量级、双向数据绑定、学习成本低
      • Vuex:状态集中管理、组件间共享
      • Element UI:美观度、主题适合
      • Zabbix:全面实时监控、报警、灵活、自动发现
  • 结构化需求分析
    • 结构化的特点:自顶向下、逐步分解、面向数据
    • 三大模型及数据字典:功能模型(数据流图)、行为模型(状态转换图)、数据模型(E-R 图)
    • 功能模型
    • 行为模型
    • 数据模型
  • 论软件架构的评估
    • 场景和需求收集
    • 体系结构视图和场景实现
    • 属性模型构造和分析
    • 折中
  • 面向服务的架构 SOA
    • 服务注册中心
      • ESB:集成了不同协议的不同服务,做了消息的转化、解释以及路由的工作,让不同的服务联通
    • 服务请求者
      • 客户端
    • 服务提供者