系统架构设计师常见高频考点总结之数据库
1. 局部数据库+缓存
1.1. 如何避免单点故障?(高可用设计)
只要题目提到“避免单点故障”或“高可靠性”,标准答案只有一套组合拳:
冗余(Redundancy):一台不够就两台。
热备(Hot Standby)/ 集群:主节点挂了,备节点立马顶上。
分布式/分片:鸡蛋不要放在一个篮子里。
本题答案套路:建立主从复制(Master-Slave)或热备份机制。局部数据库负责写,并同步给备份库;缓存负责读。
1.2. 数据的增删改查(CRUD)如何实现?(缓存策略)
这是经典的Cache-Aside Pattern(旁路缓存模式)。你必须背下来这套标准流程,软考只要考到缓存,90%都是这个逻辑。
读取(Read)逻辑:“先查缓存,再查库,最后回填”
应用先去 Cache 找。
命中(Hit):直接返回数据。
未命中(Miss):去数据库(DB)读数据。
回填:把从 DB 读到的数据写入 Cache,方便下次用。
返回数据。
写入/添加(Create)逻辑:“写库”
直接写入数据库(主库)。
注:新数据通常不需要立即写入缓存,等有人读的时候再利用“读取逻辑”加载(即延迟加载)。
更改/删除(Update/Delete)逻辑:“双更难题”
这里有一个著名的坑:是“更新缓存”还是“删除缓存”?
软考标准答案(也是业界推荐):先更新数据库,然后删除(失效)缓存。
为什么不更新缓存? 因为并发情况下容易产生脏数据。
为什么要删除? 让下一次读取请求发现缓存空了,触发“读取逻辑”去数据库拉取最新的,这样最稳。
1.3 总结:答题“公式”
1. Cache-Aside Pattern数据不一致解决方案
初始状态:缓存失效(或为空),数据库中的值为 Old_Value。
线程 2(读)接收读请求,发现缓存未命中,于是去查询数据库,读取到了 Old_Value。
注意:此时线程 2 还没有来得及把数据写入缓存,发生了网络延迟或线程切换。
线程 1(写)接收写请求,将数据库中的值更新为 New_Value。
线程 1(写)按照策略删除(淘汰)缓存中的 Key。
线程 2(读)恢复执行,将第 2 步读取到的 Old_Value 写入缓存。
最终结果:数据库中是 New_Value(新值),但缓存中却是 Old_Value(旧值)。后续的读请求都会读到旧数据,直到缓存过期。
缓存出现数据不一致。下面有3中解决方案:
延时双删策略:在更新数据库并删除缓存后,延迟一段时间再次删除缓存,以清除可能被读线程回填的脏数据。
设置缓存过期时间:为缓存设置较短的 TTL(生存时间),保证在数据不一致时,旧数据能通过过期自动修正,实现最终一致性。
使用分布式锁:在读写操作时对同一资源加锁,将并发操作串行化,保证强一致性。
2. 遇到“缓存读写策略”:
请默写以下伪代码逻辑作为答案骨架:
读数据:
if (Cache中有数据) { return Cache数据; } else { Data = 读数据库; 写入Cache(Data); return Data; }改/删数据:
expand_less1. 操作数据库(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 函数依赖
规则拿出原函数依赖集 F中的每一条依赖 X→Y,去检查分解后的选项:是否有一个小表,把X和Y里的所有属性都“包裹”进去了?
如果找到了,说明这条依赖被直接保留了。
如果 F 中的所有依赖都能在各个小表中找到各自的“归宿”,那么这个分解就100% 保持函数依赖。
结合你的题目(看选项 D):
看 A→B:A 和 B 都在 R1(ABC) 中。直接保留。
看 E→A:E 和 A 都在 R3(ACE) 中。直接保留。
看 B→D:B 在 R1(ABC) 中,D 在
R2(ED)中。它们被拆散了!
当属性被拆散到不同的表中时,直观法就失效了。被拆散不一定意味着丢失,它可能通过其他表中的依赖“间接”推导出来。
3.3 无损连接
无损连接的核心判断标准是:分解后的各个子关系通过公共属性连接,能够还原出原关系,不会产生“多余”的元组。可以使用简单的交集键判断。
随便挑两个有公共属性的子集合(比如 R1和 R2)。
用 2 集合的法则判断它们:求交集 R1∩R2。
看已知函数依赖集 F 中,这个交集能不能推导出 R1 或 R2中的所有属性(或者推导出它们的差集)。
如果能:说明 R1和 R2 的连接是无损的。我们就可以把它们合并成一个新的大集合
R12=R1∪R2。如果不能:换另外两个集合试试(比如试试 R2 和 R3)。
重复上述过程,拿新合并出来的 R12 去和剩下的 R3 继续做交集判断。
最终结果:如果所有的子集合最终能“连连看”合并成一个包含全属性的大集合 U,那就是无损连接。如果中间卡住了,谁和谁都合并不了,那就是有损连接。
观察选项 A, B, C:
A.
{𝑅1(𝐴𝐶),𝑅2(𝐸𝐷),𝑅3(𝐵)}:𝑅1和𝑅2没有公共属性,连接变成笛卡尔积,有损。B.
{𝑅1(𝐴𝐶),𝑅2(𝐸),𝑅3(𝐷𝐵)}:𝑅1和𝑅3没有公共属性,有损。C.
{𝑅1(𝐴𝐶),𝑅2(𝐸𝐷),𝑅3(𝐴𝐵)}:𝑅1和𝑅2没有公共属性,𝑅2和𝑅3也没有公共属性。这种分解是断裂的,有损。
- 分析选项 D.
{𝑅1(𝐴𝐵𝐶),𝑅2(𝐸𝐷),𝑅3(𝐴𝐶𝐸)}:连接 R1 和 R3:公共属性是 AC,可以决定R1或R3所有的属性,R1 和 R3是无损连接,合并成连接 R13。R13 和 R2:公共属性是 E可以决定R2d的所有属性。所以R13 和 R2也可以合并
4.“三级模式-两级映像”架构
1. 核心概念:三层“皮”
数据库从外到内分为三层:
外模式:也叫用户模式。
对应:视图、部分数据。
含义:用户看到的数据样子。
模式:也叫概念模式/逻辑模式。
对应:基本表。
含义:数据库整体的逻辑结构(所有表、字段、关系)。
内模式:也叫物理模式。
对应:存储文件、索引。
含义:数据在硬盘上怎么存(物理顺序、存储路径)。
2. 核心价值:两级“映像”与独立性
为了让上层不动,下层随便改,中间需要两层映射:
外模式/模式映像→ 保证逻辑独立性。
场景:当表结构(模式)改变(如增加字段),只要修改此映像,视图(外模式)不用变,应用程序也不用修。
模式/内模式映像→ 保证物理独立性。
场景:当存储结构(内模式)改变(如创建聚簇索引),只要修改此映像,表(模式)不用变。
5. Armstrong 公理系统
设关系模式 R<U, F>,U 是关系模式 R 的属性全集,F 是关系模式 R 的一个函数依赖集。对于 R<U, F>来说有以下的:
自反律:若 Y ⊆ X ⊆ U,则 X→Y 为 F 所逻辑蕴含
增广律:若 X→Y 为 F 所逻辑蕴含,且 Z ⊆ U,则 XZ→YZ 为 F 所逻辑蕴含
传递律:若 X→Y 和 Y→Z 为 F 所逻辑蕴含,则 X→Z 为 F 所逻辑蕴含
合并规则:若 X→Y, X→Z, 则 X→YZ 为 F 所蕴涵
伪传递率:若 X→Y, WY→Z, 则 XW→Z 为 F 所蕴涵
分解规则:若 X→Y, Z
⊆Y , 则 X→Z 为 F 所蕴涵
6. 反规范化技术
- 增加冗余列:在多个表中保留相同的列,通过增加数据冗余减少或避免查询时的连接操作。
- 重新组表:如果许多用户需要查看两个表连接出来的结果数据,则把这两个表重新组成一个表来减少连接而提高性能。
- 水平分割表:根据一列或多列数据的值,把数据放到多个独立的表中,主要用于表数据规模很 大、表中数据相对独立或数据需要存放到多个介质上时使用。
- 垂直分割表:对表进行分割,将主键与部分列放到一个表中,主键与其它列放到另一个表中,在查询时减少I/0次数。
7. 数据不一致性的解决方法
- 批处理维护:指对复制列或派生列的修改积累一定的时间后,运行一批处理作业或存储过程对复制或派生列进行修改,这只能在对实时性要求不高的情况下使用。
- 应用逻辑:要求必须在同一事务中对所有涉及的表进行增、删、改操作。用应用逻辑来实现数据的完整性风险较大,因为同一逻辑必须在所有的应用中使用和维护,容易遗漏,特别是在需求变化时,不易于维护。
- 触发器:对数据的任何修改立即触发对复制列或派生列的相应修改。触发器是实时的,而且相应的处理逻辑只在一个地方出现,易于维护。一般来说,是解决这类问题比较好的办法。该系统应该采用触发器。
8. 分布式数据
分布式数据库两阶段提交协议中的两个阶段是指表决阶段、执行阶段
主从复制机制的好处:提升性能(读写分离提升并发)、优良的可扩展性、提升可用性、负载均衡、提升数据安全性
9. 数据连接
1. 自然连接
符号:
(形状像一个蝴蝶结)
2. 全连接
“全连接”在不同的语境下可能有两种指代,对应的符号也不同:
指代一:全外连接
符号:⟗ (在自然连接的蝴蝶结左右两边各加一条竖线)
指代二:笛卡尔积 / 交叉连接
符号 :
(乘号)
10ORM(对象关系映射)
ORM(对象关系映射)与直接 SQL 的优缺点对比:ORM 降低学习成本、减少代码量,但复杂查询处理难、性能不如直接 SQL 。
11 数据访问层(DAL)
引入数据访问层(DAL)的原因:隔离异构数据库平台、降低耦合、逻辑更清晰 。
