系统架构设计师常见高频考点总结之数据库

1. 局部数据库+缓存

1.1. 如何避免单点故障?(高可用设计)

只要题目提到"避免单点故障"或"高可靠性",标准答案只有一套组合拳:

  • 冗余(Redundancy):一台不够就两台。

  • 热备(Hot Standby)/ 集群:主节点挂了,备节点立马顶上。

  • 分布式/分片:鸡蛋不要放在一个篮子里。

本题答案套路:建立主从复制(Master-Slave)或热备份机制。局部数据库负责写,并同步给备份库;缓存负责读。

1.2. 数据的增删改查(CRUD)如何实现?(缓存策略)

这是经典的Cache-Aside Pattern(旁路缓存模式)。你必须背下来这套标准流程,软考只要考到缓存,90%都是这个逻辑。

  • 读取(Read)逻辑"先查缓存,再查库,最后回填"

    1. 应用先去 Cache 找。

    2. 命中(Hit):直接返回数据。

    3. 未命中(Miss):去数据库(DB)读数据。

    4. 回填:把从 DB 读到的数据写入 Cache,方便下次用。

    5. 返回数据。

  • 写入/添加(Create)逻辑"写库"

    • 直接写入数据库(主库)。

    • 注:新数据通常不需要立即写入缓存,等有人读的时候再利用"读取逻辑"加载(即延迟加载)。

  • 更改/删除(Update/Delete)逻辑"双更难题"

    • 这里有一个著名的坑:是"更新缓存"还是"删除缓存"?

    • 软考标准答案(也是业界推荐)先更新数据库,然后删除(失效)缓存

    • 为什么不更新缓存? 因为并发情况下容易产生脏数据。

    • 为什么要删除? 让下一次读取请求发现缓存空了,触发"读取逻辑"去数据库拉取最新的,这样最稳。

1.3 总结:答题"公式"

1. Cache-Aside Pattern数据不一致解决方案

  1. 初始状态: 缓存失效(或为空),数据库中的值为 Old_Value。

  2. 线程 2(读) 接收读请求,发现缓存未命中,于是去查询数据库,读取到了 Old_Value。

    • 注意:此时线程 2 还没有来得及把数据写入缓存,发生了网络延迟或线程切换。
  3. 线程 1(写) 接收写请求,将数据库中的值更新为 New_Value。

  4. 线程 1(写) 按照策略删除(淘汰)缓存中的 Key。

  5. 线程 2(读) 恢复执行,将第 2 步读取到的 Old_Value 写入缓存。

  6. 最终结果: 数据库中是 New_Value(新值),但缓存中却是 Old_Value(旧值)。后续的读请求都会读到旧数据,直到缓存过期。

出现数据不一致。下面有3中解决方案:

  • 延时双删策略:在更新数据库并删除缓存后,延迟一段时间再次删除缓存,以清除可能被读线程回填的脏数据。

  • 设置缓存过期时间:为缓存设置较短的 TTL(生存时间),保证在数据不一致时,旧数据能通过过期自动修正,实现最终一致性。

  • 使用分布式锁:在读写操作时对同一资源加锁,将并发操作串行化,保证强一致性。

2. 遇到"缓存读写策略":

请默写以下伪代码逻辑作为答案骨架:

  • 读数据

    java 复制代码
    if (Cache中有数据) {
        return Cache数据;
    } else {
        Data = 读数据库;
        写入Cache(Data);
        return Data;
    }
  • 改/删数据

    expand_less

    java 复制代码
    1. 操作数据库(Update/Delete);
    2. 标记Cache失效(Delete Key); // 这一点最重要!

3. 遇到"高可用/无单点故障":

  • 答案必须包含关键词:热备份主从复制集群冗余

2. 数据库设计冲突

2.1. 命名冲突

指相同意义的属性,在不同的分E-R图上有着不同的命名,或是名称相同的属性在不同的分E-R图中代表着不同的意义,这些也要进行统一。

2.2. 属性冲突

指同一属性可能会存在于不同的分E-R中,由于设计人员不同或是出发点不同,对属性的类型、取值范围和数据单位等可能会不一致,这些属性对旧的数据将来只能以一种形式在计算机中存储,这就需要在设计阶段进行统一

2.3. 结构冲突

指同一实体在不同的分E-R图中有不同的属性,同一对象在某一分E-R图中被抽象为实体,而在另一分E-R图中又被抽象为属性

3. 函数依赖和无损连接

设关系模式R(U,F),其中R上的属性集U={A, B, C, D, E},R上的函数依赖集 F={A→B,DE→B,CB→E,E→A, B→D}。++++(1)++++ 为关系R的候选关键字。分解++++(2)++++是无损连接,并保持函数依赖的。

(7)A.AB B.DE C.CE D.DB

(8)A.p = { R1(AC), R2 (ED), R3 (B)} B.p={R1 (AC), R2 (E), R3 (DB) }

C.p={R1(AC), R2 (ED), R3 (AB)} D.p = { R1 (ABC), R2 (ED), R3 (ACE) }

3.1 函数依赖

问题 (1):寻找关系 R 的候选关键字

L-R-N 属性分类法:

  • L 类(只出现在依赖左边):C(出现在 CB→E 左边,从未出现在右边)。

    • 推论:候选关键字必须包含属性 C。
  • R 类(只出现在依赖右边):无。

  • LR 类(两边都出现):𝐴,𝐵,𝐷,𝐸。

  • N 类(两边都不出现):无。

3.2 无损连接

无损连接 的核心判断标准是:分解后的各个子关系通过公共属性连接,能够还原出原关系,不会产生"多余"的元组。可以使用 Chase 算法简单的交集键判断

  • 观察选项 A, B, C

    • A. {𝑅1(𝐴𝐶),𝑅2(𝐸𝐷),𝑅3(𝐵)}{R1(AC),R2(ED),R3(B)}𝑅1𝑅2 没有公共属性,连接变成笛卡尔积,有损

    • B. {𝑅1(𝐴𝐶),𝑅2(𝐸),𝑅3(𝐷𝐵)}{R1(AC),R2(E),R3(DB)}𝑅1𝑅3 没有公共属性,有损

    • C. {𝑅1(𝐴𝐶),𝑅2(𝐸𝐷),𝑅3(𝐴𝐵)}{R1(AC),R2(ED),R3(AB)}𝑅1𝑅2 没有公共属性,𝑅2𝑅3 也没有公共属性。这种分解是断裂的,有损

  • 分析选项 D. {𝑅1(𝐴𝐵𝐶),𝑅2(𝐸𝐷),𝑅3(𝐴𝐶𝐸)}{R1(ABC),R2(ED),R3(ACE)}:连接 R1 和 R3 :公共属性是 AC。在 R1 中有依赖 𝐴→𝐵(虽然 AC不一定是键,但有关联)。连接 R3 和 R2 :公共属性是 E。在 R3 中有依赖 E→A

  • Chase 验证:

第一步:画出初始表(初始状态)

我们需要验证的分解是选项 D:

  • R1 (A, B, C)

  • R2 (E, D)

  • R3 (A, C, E)

我们画一个表格,列是所有属性 𝐴,𝐵,𝐶,𝐷,𝐸。规则 :如果某个子模式里 这个属性,就填 a(表示有值);如果没有,就填空(或者 b,表示未知)。

(注:下标数字只是为了区分列号,a 代表"真值",b 代表"空值"。我们的目标是把某一行全变成 𝑎)

第二步:开始"填空"(应用函数依赖)

我们要利用题目给出的函数依赖 𝐹={𝐸→𝐴,𝐴→𝐵,𝐵→𝐷,...} 来推导,看能不能把空填上。

1. 利用 𝐸→𝐴

  • 观察 E 列 :发现 R2R3 都有 a5

    (也就是它俩在 E 上值相同)。

  • 推导:根据 𝐸→𝐴,既然 E 相同,那么它们的 A 也必须相同。

  • 操作:R3 的 A 是 𝑎1(真值),R2 的 A 是 𝑏21

    (空值)。我们把 R2 的 A 填上 𝑎1​

  1. 利用 𝐴→𝐵,B→D 以此类推最终得到

结论 :R3 这一行变成了 (𝑎1,𝑎2,𝑎3,𝑎4,𝑎5),全是真值,没有任何空缺。

这在数学上证明了:通过这些子关系的连接,我们可以完整地还原出原关系的所有属性,没有信息丢失。 所以,分解是无损连接的。

相关推荐
xixingzhe22 小时前
MySQL CDC实现方案
数据库·mysql
tqs_123452 小时前
tcc中的空回滚和悬挂问题
java·数据库
哪里不会点哪里.3 小时前
Spring 事务机制详解:原理、传播行为与失效场景
java·数据库·spring
IT大白3 小时前
8、MySQL相关问题补充
数据库·sql
爪哇天下3 小时前
Mysql实现经纬度距离的排序(粗略的城市排序)
数据库·mysql
独自破碎E3 小时前
MySQL中有哪些日志类型?
数据库·mysql
笨蛋不要掉眼泪3 小时前
Redis核心数据类型与命令
数据库·redis·缓存
数据知道4 小时前
一文掌握 MongoDB 详细安装与配置(Windows / Linux / macOS 全平台)
linux·数据库·windows·mongodb·macos
Knight_AL4 小时前
Flink 状态管理详细总结:State 分类、Keyed State 实战、Operator State、TTL、状态后端选型
前端·数据库·flink