软考-系统架构师-数据库系统(二)

十、数据库规范化理论

10.1、规范化理论基本概念

函数依赖定义 :设 R(U,F)R(U, F)R(U,F) 是属性 UUU 上的一个关系模式,XXX 和 YYY 是 UUU 的子集,rrr 为 RRR 的任一关系。如果对于 rrr 中的任意两个元组 uuu、vvv,只要有 u[X]=v[X]u[X]=v[X]u[X]=v[X],就有 u[Y]=v[Y]u[Y]=v[Y]u[Y]=v[Y],则称 XXX 函数决定 YYY ,或称 YYY 函数依赖于 XXX ,记为 X→YX \rightarrow YX→Y

示例学号 -> 成绩。学号是决定因素

10.2、非规范化带来的问题

数据冗余:数据重复存储。

更新异常:修改一个数据需要修改多行,易导致数据不一致。

插入异常:应该插入的数据无法插入。

删除异常:不该删除的数据被删除了。

10.3、Armstrong 公理系统

对关系模式 R<U,F>R<U, F>R<U,F> 来说有以下的推理规则

10.3.1、基础规则

自反律 (Reflexivity) :若 Y⊆X⊆UY \subseteq X \subseteq UY⊆X⊆U,则 X→YX \rightarrow YX→Y 成立。

增广律 (Augmentation) :若 Z⊆UZ \subseteq UZ⊆U 且 X→YX \rightarrow YX→Y,则 XZ→YZXZ \rightarrow YZXZ→YZ 成立。

传递律 (Transitivity) :若 X→YX \rightarrow YX→Y 且 Y→ZY \rightarrow ZY→Z,则 X→ZX \rightarrow ZX→Z 成立。

10.3.2、推导规则

合并规则 :由 X→Y,X→ZX \rightarrow Y, X \rightarrow ZX→Y,X→Z,有 X→YZX \rightarrow YZX→YZ

伪传递规则 :由 X→Y,WY→ZX \rightarrow Y, WY \rightarrow ZX→Y,WY→Z,有 XW→ZXW \rightarrow ZXW→Z

分解规则 :由 X→YX \rightarrow YX→Y 及 Z⊆YZ \subseteq YZ⊆Y,有 X→ZX \rightarrow ZX→Z

10.4、键(Key)的概念

候选键 (Candidate Key) :唯一标识元组,且无冗余(最小性)。

主键 (Primary Key) :从候选键中任选一个

外键 (Foreign Key) :其他关系的主键

主属性与非主属性 :组成候选码的属性就是主属性 ,其他的就是非主属性

全码 (All-Key):关系模式的所有属性组是这个关系的候选码。
消除冗余属性
DBA选定
组成部分
不包含在候选键中
属性集合
超键/Super Key

唯一标识元组
候选键/Candidate Key

唯一 + 最小
主键/Primary Key

被选中的那个
主属性
非主属性

10.5、求候选键的方法(图论法)


不能

不能
开始求解候选键
绘制函数依赖有向图
寻找入度为0的属性集合 Set_A
Set_A 能否遍历

图中所有节点?
Set_A 即为候选键
选取中间节点 Node_M

(既有入度也有出度)
Set_B = Set_A + Node_M
Set_B 能否遍历

图中所有节点?
Set_B 为候选键
尝试其他中间节点组合

1)将关系模式的函数依赖关系用"有向图"的方式表示。

2)找入度为 0 的属性,并以该属性集合为起点,尝试遍历有向图。

3)若能正常遍历图中所有节点,则该属性集即为关系模式的候选键

4)若入度为 0 的属性集不能遍历图中所有节点,则需要尝试性地将一些中间节点(既有入度,也有出度的节点)并入入度为 0 的属性集中,直至该集合能遍历所有节点,集合为候选键。

10.6、属性闭包 (X+X^+X+) 计算法

定义 :设 FFF 为函数依赖集,XF+{X}^+_FXF+ 是指所有被 XXX 逻辑蕴含的属性集合。

算法

  1. 设 result=Xresult = Xresult=X。
  2. 遍历 FFF 中的依赖 A→BA \rightarrow BA→B,如果 A⊆resultA \subseteq resultA⊆result,则 result=result∪Bresult = result \cup Bresult=result∪B。
  3. 重复直到 resultresultresult 不再增长。

10.7、快速求候选键(分类法)

L 类 (Left) :只在函数依赖左边 出现的属性。→\rightarrow→ 必是候选键的成员。

R 类 (Right) :只在函数依赖右边 出现的属性。→\rightarrow→ 不可能是候选键的成员(除非是全码)。

N 类 (None) :在左右两边都没 出现的属性。→\rightarrow→ 必是候选键的成员(通常是独立的无关属性)。

LR 类 (Left & Right) :在左右两边 出现的属性。→\rightarrow→ 可能是候选键的成员。

解题步骤

  1. 令 K=L∪NK = L \cup NK=L∪N。
  2. 计算 KKK 的闭包 K+K^+K+。
  3. 若 K+K^+K+ 包含所有属性,则 KKK 是唯一候选键。
  4. 若 K+K^+K+ 不全,则依次尝试将 LRLRLR 类属性加入 KKK 中计算闭包,直至覆盖所有属性。

10.8、规范化与性能的权衡

"异常" (冗余、插入、删除、更新)是我们在逻辑设计阶段力求避免的,所以我们要拆分表(规范化)。

反规范化 :但是,作为架构师,在海量数据查询场景下(如数据仓库、高并发读),为了减少 Join 操作,我们可能会故意违反规范化,保留冗余数据(如订单表存一份商家名称)。这是"空间换时间"的经典架构决策。

十一、数据库范式

11.1、范式升级路线图

规范化过程
No
Yes
No
Yes
No
Yes
No
Yes
初始关系模式
是否所有属性

均为原子值?
非 1NF
第一范式 1NF
是否消除了非主属性

对码的部分依赖?
存在部分依赖
第二范式 2NF
是否消除了非主属性

对码的传递依赖?
存在传递依赖
第三范式 3NF
是否消除了主属性

对码的部分/传递依赖?
主属性内部存在依赖
BC范式

第一范式 (1NF) :属性值都是不可再分的原子值

第二范式 (2NF) :在 1NF 基础上,消除非主属性对候选键的部分依赖

第三范式 (3NF) :在 2NF 基础上,消除非主属性对候选键的传递依赖

BC范式 (BCNF) :在 3NF 基础上,消除主属性对候选键的部分和传递依赖(即:每个依赖的决定因素必定包含某个候选码)。

:4NF 是限制关系模式的属性间不允许有非平凡且非函数依赖的多值依赖。

11.2、第一范式 (1NF)

定义:所有域只包含原子值,即每个属性都是不可再分的数据项。

反例{系名称, 高级职称人数: {教授: x, 副教授: y}}。这里"高级职称人数"包含了两个子属性,违反 1NF。

11.3、第二范式 (2NF)

成绩表
联合决定
联合决定
单独决定
学号
成绩
课程号
学分
红色箭头表示部分依赖

非主属性'学分'只依赖于主键的一部分

定义 :当且仅当实体 E 是 1NF,且每一个非主属性完全依赖主键(不存在部分依赖)时。

反例模型(学号, 课程号, 成绩, 学分)。主键是 (学号, 课程号)

问题学分 只依赖于 课程号(主键的一部分),属于部分依赖

解决方案 :拆分为 R1(课程号, 学分)R2(学号, 课程号, 成绩)

11.4、第三范式 (3NF)

决定
决定
决定
学号
系号
系名
系位置
传递依赖:

学号 -> 系号 -> 系名

定义 :当且仅当实体 E 是 2NF,且 E 中没有非主属性传递依赖于码时。

反例模型(学号, 姓名, 系号, 系名, 系位置)。主键是 学号

问题学号 -> 系号系号 -> 系名。存在 学号 -> 系名传递依赖

解决方案 :拆分为 R1(学号, 姓名, 系号)R2(系号, 系名, 系位置)

11.5、BC范式 (BCNF)

定义 :设 R 是一个关系模式,F 是它的依赖集,R 属于 BCNF 当且仅当其 F 中每个依赖的决定因素必定包含 R 的某个候选码

BCNF 定律"所有的决定因素,必须是候选键(一把完整的钥匙)。"

反例模型STJ (Student, Teacher, Subject)

  • 规则:每个老师只教一门课 (T→JT \rightarrow JT→J);每门课有若干老师;学生选定某门课就对应一个固定老师。
  • 候选键:(S,T)(S, T)(S,T) 和 (S,J)(S, J)(S,J)。
  • 问题 :存在依赖 T→JT \rightarrow JT→J。这里 TTT(老师)决定了 JJJ(课程),但 TTT 不是候选键(它只是候选键的一部分)。
  • 插入异常 :如果新来了一个老师,还没排课(没学生选),他的信息(老师-课程)插不进去,因为缺 SSS(主键不能为空)。
  • 删除异常:如果选这门课的学生都退课了,删掉学生记录的同时,把"这个老师教这门课"的信息也误删了。
  • 结论:虽然它是 3NF(没有非主属性),但它不是 BCNF。

关系模式 STJ (学生, 老师, 课程)
联合组成候选键
联合组成候选键
决定
❌ 决定因素 T 不是候选键
学生 S
课程 J
老师 T
候选键: S+J

11.6、概念辨析

11.6.1、部分依赖

A,B→CA, B \rightarrow CA,B→C,同时 A→CA \rightarrow CA→C。说明 CCC 只需要 AAA 就够了,不需要 BBB。即 CCC 部分依赖于 (A,B)(A, B)(A,B)。这是 2NF 要解决的问题。

识别技巧 :只要主键是组合键(由多个属性构成),就极大概率存在部分依赖,需要检查 2NF。

11.6.2、传递依赖

A→B,B→CA \rightarrow B, B \rightarrow CA→B,B→C。说明 CCC 是通过 BBB 间接依赖于 AAA 的。这是 3NF 要解决的问题。

识别技巧:只要表中存在"非主属性"决定"非主属性"的情况,就违反了 3NF。

11.6.3、3NF 与 BCNF 的区别

3NF :只盯着**"非主属性"看。只要非主属性都很规矩(既不部分依赖,也不传递依赖),那就是 3NF。它允许主属性**之间存在依赖混乱。

BCNF :是 3NF 的加强版 。它不仅要求非主属性规矩,还要求主属性也很规矩。

11.7、模式分解

11.7.1、保持函数依赖分解

定义 :设数据库模式 ρ={R1,...,Rk}\rho=\{R_1, ..., R_k\}ρ={R1,...,Rk} 是 R 的一个分解,F 是 R 上的函数依赖集。如果 {F1,F2,...,Fk}\{F_1, F_2, ..., F_k\}{F1,F2,...,Fk} 的并集与 F 是等价 的(即相互逻辑蕴涵),那么称分解 ρ\rhoρ 保持 FD。
检查过程


输入: 原模式 R, 依赖集 F
分解为 R1, R2
投影: 找出 R1 中的依赖 F1
投影: 找出 R2 中的依赖 F2
合并: F' = F1 ∪ F2
是否等价于 F?
保持函数依赖
不保持函数依赖

(丢失了约束)

实例分析

  • 例1 (保持) :F={A→B,B→C}F=\{A \rightarrow B, B \rightarrow C\}F={A→B,B→C}。分解为 (AB),(BC)(AB), (BC)(AB),(BC)。
    • R1(AB)R_1(AB)R1(AB) 包含 A→BA \rightarrow BA→B。
    • R2(BC)R_2(BC)R2(BC) 包含 B→CB \rightarrow CB→C。
    • 原有的依赖都还在,保持
  • 例2 (不保持) :F={A→B,B→C,A→C}F=\{A \rightarrow B, B \rightarrow C, A \rightarrow C\}F={A→B,B→C,A→C}。分解为 (AB),(AC)(AB), (AC)(AB),(AC)。
    • R1(AB)R_1(AB)R1(AB) 包含 A→BA \rightarrow BA→B。
    • R2(AC)R_2(AC)R2(AC) 包含 A→CA \rightarrow CA→C。
    • 丢失 :B→CB \rightarrow CB→C 丢失了(因为 B 和 C 不在同一张表里,且无法通过其他依赖推导出来)。
    • 结果:不保持

11.7.2、无损分解

有损:不能还原。

无损:可以还原。

定义 :将一个关系模式分解成若干个关系模式后,通过自然连接投影等运算仍能还原到原来的关系模式。

11.7.2.1、公式法

前提 :将关系模式 R 分解为 ρ={R1,R2}\rho = \{R_1, R_2\}ρ={R1,R2}。

判定公式:分解 ρ\rhoρ 具有无损连接性的充分必要条件是:

(R1∩R2)→(R1−R2)(R_1 \cap R_2) \rightarrow (R_1 - R_2)(R1∩R2)→(R1−R2)

或者

(R1∩R2)→(R2−R1)(R_1 \cap R_2) \rightarrow (R_2 - R_1)(R1∩R2)→(R2−R1)
I -> D1 成立
I -> D2 成立
都不成立
开始: 分解 R 为 R1 和 R2
计算交集 I = R1 ∩ R2
计算差集

D1 = R1 - R2

D2 = R2 - R1
判定:

公共属性 I 能否决定

D1 或 D2 ?
无损分解

(Lossless)
有损分解

(Lossy)

例题 :设 R=ABC,F={A→B}R=ABC, F=\{A \rightarrow B\}R=ABC,F={A→B}。

分解1 :ρ1={R1(AB),R2(AC)}\rho_1 = \{R_1(AB), R_2(AC)\}ρ1={R1(AB),R2(AC)}

  • 交集:AAA。
  • R1−R2=BR_1 - R_2 = BR1−R2=B。
  • 判断:A→BA \rightarrow BA→B 成立吗?成立
  • 结果:无损分解

分解2 :ρ2={R1(AB),R3(BC)}\rho_2 = \{R_1(AB), R_3(BC)\}ρ2={R1(AB),R3(BC)}

  • 交集:BBB。
  • R1−R3=AR_1 - R_3 = AR1−R3=A;R3−R1=CR_3 - R_1 = CR3−R1=C。
  • 判断:B→AB \rightarrow AB→A?不成立。B→CB \rightarrow CB→C?不成立。
  • 结果:有损分解(无法还原)。
11.7.2.2、表格法

题目背景:

给定关系模式 成绩 (学号, 姓名, 课程号, 课程名, 分数)。

函数依赖集 FFF:

  1. 学号→姓名学号 \rightarrow 姓名学号→姓名
  2. 课程号→课程名课程号 \rightarrow 课程名课程号→课程名
  3. (学号,课程号)→分数(学号, 课程号) \rightarrow 分数(学号,课程号)→分数

将其分解为 3 个子模式:

  • R1 成绩:(学号, 课程号, 分数)
  • R2 学生:(学号, 姓名)
  • R3 课程:(课程号, 课程名)

问题:判断该分解是否为无损分解?

第一步:初始化表格

画一张表, 代表所有属性,代表分解后的各个子模式。

  • 如果某个子模式包含该属性,填入 ✔。
  • 如果不包含,填入 ×。
学号 姓名 课程号 课程名 分数
成绩 (R1) ×\times× ×\times×
学生 (R2) ×\times× ×\times× ×\times×
课程 (R3) ×\times× ×\times× ×\times×

第二步:根据函数依赖"填空"

依次扫描函数依赖集 FFF,如果在某个属性列上,多行都打钩了(即值相等),那么它们在依赖推导出的那一列上的值也必须相等(把 ×\times× 变成 ✔)。

1. 应用依赖:学号→姓名学号 \rightarrow 姓名学号→姓名
  • 观察 :"学号"列,R1R2 都有 ✔✔(说明它们针对同一个学号)。
  • 推导:既然学号相同,那么"姓名"也必须相同。
  • 操作 :R2 在"姓名"列是 ✔,所以我们要把 R1 的"姓名"列也改成 ✔
2. 应用依赖:课程号→课程名课程号 \rightarrow 课程名课程号→课程名
  • 观察 :"课程号"列,R1R3 都有 ✔。
  • 推导:既然课程号相同,那么"课程名"也必须相同。
  • 操作 :R3 在"课程名"列是 ✔,所以我们要把 R1 的"课程名"列也改成 ✔
学号 姓名 课程号 课程名 分数
成绩 (R1) ✔ (新补) ✔ (新补)
学生 (R2) ×\times× ×\times× ×\times×
课程 (R3) ×\times× ×\times× ×\times×

判定标准:

只要表格中有一行全部变成了 ✔(即该行不再有 ×\times×),则证明该分解是无损连接。

本题结果

  • 观察上表,第一行(成绩 R1)的所有列现在都是 ✔ 了。
  • 结论:该分解是无损分解。

11.8、反规范化

11.8.1、核心概念

反规范化 (又称逆规范化、违规化)是指在数据库设计中,为了提高查询性能 ,有意违反规范化原则(如 3NF),通过增加数据冗余合并表等手段,以减少连接操作(Join)的过程。

  • 本质空间换时间
  • 适用场景读多写少、数据量大、对查询响应速度要求极高的系统(如数据仓库、报表系统、高并发互联网应用)。
  • 所属阶段 :属于逻辑结构设计阶段(因为它改变了数据的Schema结构)。

反规范化 (读优化)
合并
合并
解决
解决
解决
规范化 (写优化)
拆分
拆分
拆分
大宽表
表A
表B
表C
目标: 消除冗余

避免异常

节省空间
表A
冗余大表
表B
目标: 减少Join

提高查询速度

空间换时间
增加冗余列
增加派生列
表分割
风险: 数据不一致
触发器
应用层同步
批处理

11.8.2、常见的反规范化技术手段

技术手段 说明 典型场景
增加冗余列 在一个表中复制另一个表的列。 在"订单表"中存储"客户姓名",避免每次查询订单都 Join "客户表"。
增加派生列 增加可以通过其他列计算得出的列。 在"订单明细"中有"单价"和"数量",额外增加"总价"列,避免查询时实时计算。
重新组表 把拆分过于细碎的表合并。 将 1:1 扩展表合并回主表,减少 Join。
分割表 水平分割 :按行分(如按年份分表、按地区分表)。 垂直分割:按列分(将常用列和冷门大字段分开)。 长沙的用户存在长沙表,上海的用户存在上海表(分库分表的基础)。

11.8.3、优缺点权衡

维度 优点 (Pros) 缺点 (Cons)
查询性能 连接操作(Join)少,检索容易,检索快,统计快。 -
写入性能 - 插入、更新、删除开销更大(因为要修改多份数据)。
存储成本 - 数据冗余,需要更大的存储空间。
数据质量 - 数据不一致风险(修改了A处,忘了改B处)。
开发维护 只需要查单表,SQL简单。 更新和插入的代码更难写(需要保证同步)。

11.8.4、数据不一致的解决方案

11.8.4.1、触发器 (Triggers)

利用数据库自身的触发器机制,当源数据修改时,同步更新冗余数据。

优点:实时性强。

缺点:影响写性能,逻辑隐藏在库中不易维护。

11.8.4.2、应用程序同步

在应用代码(Service层)中,在一个事务里同时更新多张表。

优点:逻辑清晰。

缺点:代码复杂度增加。

11.8.4.3、批处理 (Batch Processing)

定期(如每天凌晨)运行脚本同步数据。

适用:对实时性要求不高的统计报表。

11.8.4.4、物化视图 (Materialized View)

数据库提供的特性,自动维护查询结果的物理副本。

十二、数据库安全性控制

12.1、**"洋葱模型"**的安全防御体系

数据库安全防御体系
(1) 用户标识与鉴定

(门禁: 账号/密码)
(2) 存取控制

(权限: DAC/MAC)
(3) 视图与加密

(隐蔽: 隐藏敏感数据)
核心数据
(5) 审计 Audit

(监控摄像机: 记录所有操作)
用户

12.2、用户标识和鉴定

地位:最外层的安全保护措施。

手段:用户账户、口令(Password)、随机数检验等。

通俗理解:类似于公司的"门禁卡",证明你是谁。

12.3、存取控制

定义:对用户进行授权。

要素

  • 操作类型 :如 SELECT, INSERT, DELETE, UPDATE
  • 数据对象:数据范围(表、列、行)。

SQL (对应 GRANT / REVOKE 语句)。

12.3.1、自主存取控制 (DAC, Discretionary Access Control)

用户对自己创建的数据有控制权,并且可以将权限转授给别人

SQL实现GRANT SELECT ON TableA TO UserB WITH GRANT OPTION

缺点:权限容易扩散,安全性较低。

12.3.2、强制存取控制 (MAC, Mandatory Access Control)

系统强制规定权限,用户不能转授

机制 :给数据打标签(绝密、机密、公开),给用户打标签。只有"用户级别 ≥\ge≥ 数据级别"才能读。

优点:安全性极高(军用级)。

12.4、密码存储和传输

对象:对远程终端信息、敏感数据进行加密。

通俗理解:防止数据在传输过程中被截获(中间人攻击)。

12.5、视图的保护

机制:对视图进行授权,而不是直接对基表授权。

作用:隐藏敏感字段(例如:只给财务看"工资列",不给其他部门看)。

12.6、审计

机制 :使用专用文件或数据库,自动将用户对数据库的所有操作记录下来。

作用:事后追责,"谁在什么时间做了什么坏事"。

审计虽然能追踪责任,但会严重降低数据库性能(因为每一步操作都要写日志)。

架构决策:通常只开启关键业务(如资金变动、权限变更)的审计,而不是全量审计。

12.7、可靠

持久性、可靠性、备份 :这其实属于数据库恢复技术的范畴,但常与安全性一起考。安全性防"坏人",可靠性/备份防"故障"。

12.8、SQL 注入与参数化查询

防御:使用**预编译语句(PreparedStatement)**或存储过程,避免拼接 SQL 字符串。

十三、数据库并发控制

13.1、核心基础:事务

特性 英文 含义 关键保障机制
原子性 Atomicity 要么全做,要么全不做(All or Nothing)。 Undo Log (回滚日志)
一致性 Consistency 事务前后,数据必须保持一致状态(如转账总额不变)。 完整性约束 + 其他三个特性
隔离性 Isolation 并发事务之间互不干扰。 锁 (Locking) / MVCC
持久性 Durability 一旦提交,修改就是永久的(即使断电)。 Redo Log (重做日志)

13.2、并发产生的三大问题

13.2.1、丢失更新 (Lost Update)

场景:T1 和 T2 同时读入 A=10,T1 改为 11 提交,T2 改为 12 提交。T1 的修改被 T2 覆盖了。

后果:严重的数据错误,必须绝对避免。

13.2.2、读"脏"数据 (Dirty Read)

场景:T1 修改 A=20(未提交),T2 读到了 A=20。结果 T1 回滚了(A变回10),T2 读到的 20 就是脏数据。

后果:使用了无效数据。

13.2.3、不可重复读 (Non-repeatable Read)

场景:T1 读 A=10。此时 T2 将 A 修改为 20 并提交。T1 再次读 A,变成了 20。

后果 :同一个事务内,两次读取结果不一致(针对修改操作)。

13.2.4、幻读 (Phantom Read)

T1 读只有3行记录,T2 插入了一行,T1 再读发现变成了4行(针对新增/删除操作)。

13.3、解决方案:封锁协议

13.3.1、锁的类型

X 锁 (Exclusive Lock, 排他锁/写锁)

  • 如果 T 对 A 加了 X 锁,T 可以读写 A。
  • 其他人不能加任何锁(不能读也不能写)。

S 锁 (Shared Lock, 共享锁/读锁)

  • 如果 T 对 A 加了 S 锁,T 只能读 A。
  • 其他人 :只能加 S 锁(可以一起读),不能加 X 锁(不能写)。

13.3.2、三级封锁协议

协议级别 规则 (怎么加锁) 解决的问题 遗留的问题
一级封锁 修改数据前加 X锁,直到事务结束才释放。 丢失更新 脏读、不可重复读
二级封锁 一级 + 读取数据前加 S锁读完立刻释放 ✅ 丢失更新 ✅ 读脏数据 不可重复读
三级封锁 一级 + 读取数据前加 S锁直到事务结束才释放 ✅ 丢失更新 ✅ 读脏数据 ✅ 不可重复读 -

13.4、一级封锁协议:解决"丢失更新"

规则 :事务 T 在修改数据 前必须加 X锁 ,直到事务结束才释放。

【场景还原】 假设 T1 和 T2 都要把 A (原本是10) 加 1。

  • 如果没有协议:T1 读 10,T2 读 10。T1 改为 11 提交。T2 改为 11 提交。结果是 11(T1 的更新丢了)。
  • 有一级协议后
    1. T1 想改 A,先申请 X锁。成功。
    2. T2 也想改 A,申请 X锁
    3. 冲突! 因为 T1 拿着 X 锁,T2 的申请被拒绝,T2 进入等待队列
    4. T1 改完 A=11,提交事务,释放 X 锁。
    5. T2 终于拿到了 X 锁,读取 A(此时已经是11了),改为 12,提交。
    6. 结果 :A = 12。丢失更新被解决。

13.5、二级封锁协议:解决"读脏数据"

规则 :在一级基础上,读取数据 前必须加 S锁读完立刻释放

【场景还原】

假设 T1 修改了 A=20(还没提交),T2 想读 A。

  • 如果没有协议:T2 读到了 20。结果 T1 后来回滚了,A 变回 10。T2 读了个寂寞(脏数据)。
  • 有二级协议后
    1. T1 修改 A,根据一级协议,T1 必须加 X锁
    2. T2 想读 A,根据二级协议,T2 必须申请 S锁
    3. 冲突! T1 的 X 锁排斥 T2 的 S 锁。
    4. 机制生效 :T2 读不了!只能等着 T1 完事。
    5. 只有等 T1 提交(A确实变成20)或者回滚(A变回10)并释放 X 锁后,T2 才能拿到 S 锁去读。
    6. 结果 :T2 永远读不到"未提交"的数据。脏读被解决。

【可重复读无法避免的场景还原】

目标:T1 想读两次 A,希望两次读到的结果一样(即可重复读)。

协议背景二级封锁协议 (写加 X 锁长持,读加 S 锁,读完立刻释放)。

  1. 第一步(T1 读数据):
    • T1 发起读取 A(假设 A=10)。
    • 遵守二级协议 :T1 申请 S锁
    • 读操作:T1 读到了 A=10。
    • 关键动作 :读完了,T1 立刻释放 S锁。(注意!这时候 A 身上已经没有任何锁了,变成"裸奔"状态)。
  2. 第二步(T2 趁虚而入):
    • T1 的事务还没结束(可能在处理其他逻辑),此时 T2 进来了。
    • T2 想把 A 改成 20。
    • 遵守一级协议 :T2 申请 X锁
    • 能申请到吗? 能! 因为刚才第一步里,T1 已经把 S 锁扔掉了。A 身上没锁,T2 顺利加上 X 锁。
    • T2 修改 A=20,提交事务,释放 X 锁。
  3. 第三步(T1 再次读数据):
    • T1 的事务还在进行,它想确认一下 A 的值。
    • 遵守二级协议 :T1 再次申请 S锁
    • 能申请到吗? 能,因为 T2 已经搞完跑路了。
    • 读操作 :T1 读到了 A=20

结局 : T1 在同一个事务里,第一次读是 10,第二次读是 20。数据变了! 这就是"不可重复读"。

13.6、三级封锁协议:解决"不可重复读"

规则 :在一级基础上,读取数据 前必须加 S锁 ,直到事务结束才释放

【场景还原】 假设 T1 先读 A=10,想过一会儿再读一次验证;T2 想把 A 改成 20。

  • 有三级协议后
    1. T1 第一次读 A,申请 S锁
    2. 关键点 :读完后,T1 不释放 S锁,一直攥在手里。
    3. T2 想改 A,申请 X锁
    4. 冲突! 虽然 S 锁允许别人读,但绝对不允许别人写(X锁)。T2 发现 A 上面有 S 锁,只能等待。
    5. T1 第二次读 A,因为没人能改动 A,读出来肯定还是 10。
    6. T1 事务结束,释放 S 锁。T2 这才拿到 X 锁去修改。
    7. 结果 :在 T1 整个事务期间,没人能修改它读过的数据。不可重复读被解决。

13.7、问题协议复现图

封锁协议解决方案
并发产生的问题
解决
解决
解决
解决
解决
解决
重要结论
保证
两段锁协议 2PL
可串行化调度
(1) 丢失更新
(2) 读脏数据
(3) 不可重复读
一级封锁协议

(写加X锁, 事务结束放)
二级封锁协议

(一级 + 读加S锁, 读完马上放)
三级封锁协议

(一级 + 读加S锁, 事务结束放)

13.8、两段锁协议 (2PL)

定义:所有事务必须分两个阶段对数据项加锁和解锁。

扩展阶段:获得锁,不能释放锁。

收缩阶段:释放锁,不能获得锁。

结论若并发执行的所有事务均遵守两段锁协议,则对这些事务的任何并发调度策略都是"可串行化"的

13.9、死锁 (Deadlock)

当两个事务相互等待对方释放锁时,就会形成死锁。

  1. 预防死锁
    • 一次封锁法(一次性申请所有锁,效率低)。
    • 顺序封锁法(按固定顺序申请锁)。
  2. 死锁的诊断与解除
    • 超时法
    • 等待图法 (Wait-for Graph) :如果图中出现回路,说明有死锁。
    • 解除 :选择一个处理代价最小的事务进行撤销 (Rollback),释放它的锁给别人用。

十四、分布式事务管理

14.1、核心概念

在分布式系统中,为了保证跨多个节点的操作要么全部成功,要么全部失败(ACID特性),引入了协调者 (Coordinator) 来统一调度所有参与者 (Participant) 的执行逻辑。

最经典的一致性协议就是 2PC (Two-Phase Commit)

14.2、2PC协议流程

14.2.1、第一阶段:表决阶段 (Voting Phase)

  • 目的:形成一个共同的决定。
  • 动作:协调者问参与者:"你们准备好提交了吗?"(Prepare)。
  • 参与者行为:执行事务操作,将 Undo/Redo 信息写入日志,但不提交。如果成功写盘则返回"Ready/Yes",否则返回"Abort/No"。

14.2.2、第二阶段:执行阶段 (Execution Phase)

  • 目的:实现协调者的决定。
  • 动作
    • 如果所有人都说 Yes →\rightarrow→ 发送 Global Commit
    • 只要有一个人说 No →\rightarrow→ 发送 Global Abort
  • 参与者行为:根据指令正式提交或回滚,并释放资源(锁)。

14.3、2PC全局提交规则(铁律)

一票否决权 :只要有一个 参与者撤销事务(或超时未响应),协调者就必须做出全局撤销 (Global Abort) 的决定。

全票通过权 :只有所有 参与者都同意提交事务,协调者才能做出全局提交 (Global Commit) 的决定。

14.4、2PC详细交互与日志机制

在 2PC 中,写日志 (Write Log) 的时机非常关键,这是为了在系统崩溃后能恢复状态。

14.4.1、协调者 (Coordinator) 的逻辑

  • Start :写入 begin_commit 日志,发送"准备提交"请求,进入等待 (Wait) 状态。
  • Decision
    • 若收集齐所有"Yes" →\rightarrow→ 写入 commit 日志 →\rightarrow→ 发送"全局提交"。
    • 若收到任一"No"或超时 →\rightarrow→ 写入 abort 日志 →\rightarrow→ 发送"全局撤销"。
  • End :收到所有参与者的确认 (Ack) 后,写入 end_trans 日志,事务结束。

14.4.2、参与者 (Participant) 的逻辑

  • Receive:收到"准备提交"请求。
  • Check
    • 若无法执行 →\rightarrow→ 写入 abort 日志 →\rightarrow→ 发送"建议撤销 (No)"。
    • 若执行成功 →\rightarrow→ 写入 ready 日志 →\rightarrow→ 发送"建议提交 (Yes)" →\rightarrow→ 进入就绪 (Ready) 状态(注意:此时参与者处于阻塞状态,必须等待协调者的命令)。
  • Action
    • 收到 Commit →\rightarrow→ 写入 commit 日志 →\rightarrow→ 提交事务 →\rightarrow→ 发送 Ack。
    • 收到 Abort →\rightarrow→ 写入 abort 日志 →\rightarrow→ 回滚事务 →\rightarrow→ 发送 Ack。

参与者 (Participant) 协调者 (Coordinator) 参与者 (Participant) 协调者 (Coordinator) 1. 写 begin_commit 日志 进入 WAIT 状态 写 abort 日志 写 ready 日志 进入 READY 状态 (阻塞等待) alt [参与者执行失败] [参与者执行成功] 决策阶段 (一票否决) 4. 写 commit 日志 写 commit 日志 ->> 提交 ->> 释放资源 4. 写 abort 日志 写 abort 日志 ->> 回滚 ->> 释放资源 alt [决定提交 (全票Yes)] [决定回滚 (有No或超时)] 收到所有Ack ->> 写 end_trans 日志 2. 发送: 准备提交? (Prepare) 3. 回复: 不行 (No) 3. 回复: 可以 (Yes) 5. 发送: 全局提交 (Global Commit) 6. 回复: 完成 (Ack) 5. 发送: 全局回滚 (Global Abort) 6. 回复: 完成 (Ack)

十五、数据备份

15.1、备份方式分类

特性 冷备份 (Cold/Static Backup) 热备份 (Hot/Dynamic Backup)
别名 静态备份 动态备份
定义 关闭数据库,在停止状态下复制文件。 数据库正常运行状态下,利用备份软件进行备份。
优点 1. 简单 :只需复制文件。 2. 快速 :直接文件拷贝速度快。 3. 安全:容易归档,低度维护。 1. 高可用无需停机 ,业务不中断。 2. 精度 :可达到秒级恢复。 3. 范围:可对几乎所有数据库实体恢复。
缺点 1. 需停机 :业务必须中断。 2. 只能恢复到某个时间点。 3. 速度受限于磁盘IO。 1. 维护复杂:不能出错,否则备份无效。 2. 若失败,后果严重。
场景 对业务连续性要求不高的系统(如内部统计系统)。 7x24小时核心业务系统(如电商、银行)。

15.2、备份策略分类

备份类型 定义 (关键区别) 备份速度/空间 恢复速度
完全备份 (Full Backup) 备份所有数据。 🐢 最慢 / 最大 🚀 最快 (只需导入这一份)
差量备份 (Differential) 备份上一次完全备份之后变化的数据。 ⚖️ 中等 ⚖️ 中等 (需导入:完全备份 + 最新的一份差量)
增量备份 (Incremental) 备份上一次备份 (无论是全备、差备还是增备) 之后变化的数据。 🚀 最快 / 最小 🐢 最慢 (需导入:完全备份 + 所有中间的增量备份)

15.3、备份结构概览

备份策略_按数据量
备份方式_按状态
冷备份: 关机, 简单, 有宕机
热备份: 开机, 复杂, 无宕机
完全备份: 所有数据
差量备份: 差异自上一次'全备'
增量备份: 差异自上一次'任何备份'
备份最慢, 恢复最快
备份中等, 恢复中等
备份最快, 恢复最慢
数据库
日志文件

15.4、日志文件

定义:针对数据库改变所作的记录,独立保存在文件中。

作用:是数据库恢复的核心依据(配合 Checkpoint、Redo/Undo 使用)。

考点 :备份不仅要备数据文件 (.mdf/.dbf),必须同时备份日志文件,否则只能恢复到过去的时间点,无法恢复到故障前一秒的状态。

15.5、备份结论

15.5.1、恢复速度排序

完全备份 > 差量备份 > 增量备份。

理由:完全备份恢复时文件最少;增量备份恢复时像"拼图",要按顺序一份份拼回去,缺一不可。

15.5.2、备份速度排序

增量备份 > 差量备份 > 完全备份。

理由:增量备份的数据量通常最小。

15.5.3、推荐策略

通常建议 "每周一次全备 + 每日一次增量/差量"

如果空间紧张:选增量。

如果恢复时间要求高 (RTO短):选差量(因为恢复时不需要重放那么多份日志)。

十六、数据库故障与恢复体系

16.1、故障分类与恢复策略表

故障类型 典型原因 谁来恢复? 恢复策略 (核心考点)
事务内部故障 (可预期) 逻辑判断(如余额不足)。 程序员 在代码中预先设置 ROLLBACK 语句回滚。
事务内部故障 (不可预期) 算术溢出、死锁、违反约束。 DBMS 自动撤销 (UNDO) 该事务对数据库的修改,回退到初始状态。
系统故障 (Soft Crash) CPU断电、操作系统崩溃。 (内存数据丢,磁盘还在) DBMS 重启时自动进行。 1. 撤销 (UNDO) 未提交的事务。 2. 重做 (REDO) 已提交的事务。 *通常配合**检查点 (Checkpoint)*使用。
介质故障 (Hard Crash) 磁盘损坏、磁头碰撞。 (物理存储坏了) DBA 人工介入 。 1. 装入最新的数据库备份 (Backup) 。 2. 装入日志文件,重做 (REDO) 备份后的所有已提交业务。

16.2、恢复流程逻辑图

有 Commit 记录
无 Commit 记录
系统故障重启 / 恢复开始
正向扫描日志文件
检查事务状态
加入 REDO 队列

(重做)
加入 UNDO 队列

(撤销)
执行重做:

将日志的新值写入库
执行撤销:

将日志的旧值写回库
系统恢复完成

对外开放服务

16.3、UNDO (撤销)

对象 :故障发生时,尚未 Commit 的活跃事务。

逻辑:原子性要求"要么全做,要么全不做"。既然没做完就被打断了,那就必须抹去它所有的痕迹。

16.4、REDO (重做)

对象 :故障发生时,已经 Commit 的事务。

逻辑:持久性要求"一旦提交,永久生效"。即使数据还在内存里没来得及刷到磁盘就断电了,重启后也要根据日志把它重新写进去。

16.5、DBA (数据库管理员)

只有 介质故障 (硬盘坏了) 才需要 DBA 物理出现去换硬盘、导备份。其他故障都是 DBMS 自动完成的。

十七、数据库性能优化

17.1、优化体系总览

数据库优化不仅仅是改改 SQL 语句,而是一个从硬件到软件的系统工程 。课件将其分为两大类:集中式数据库优化分布式数据库优化

17.2、集中式数据库优化

17.2.1、硬件与系统级优化

硬件系统

  • CPU:计算密集型操作(如复杂计算、聚合)。
  • 内存:数据库缓存(Buffer Pool)的大小直接影响性能。
  • I/O (硬盘/阵列):数据库的瓶颈通常在磁盘 I/O。使用 SSD、RAID 10 等技术。
  • 网络:带宽和延迟。

系统软件

  • 调整操作系统参数,如进程优先级CPU 使用权内存分配策略(避免 Swap 交换分区的使用)。

17.2.2、数据库设计优化

表与视图

  • 表的规划:合理范式与反规范化。
  • 物化视图 (Materialized View) : 与普通视图不同,物化视图是实际存储数据 的。它预先计算并保存复杂查询的结果,以空间换时间,特别适合用于 OLAP(报表)系统。

索引 (Index)

  • 原则常查询 的列 →\rightarrow→ 建索引常修改 的列 →\rightarrow→ 避免建索引(因为维护索引有开销)。

SQL 优化:(代码级优化的黄金法则)

  1. 子查询优化 :用不相关 子查询替代相关子查询(相关子查询会执行多次)。
  2. 列的筛选 :只检索需要的列,避免 SELECT \*
  3. 条件转换 :用带 IN 的条件子句等价替换 OR 子句(OR 往往会导致索引失效)。
  4. 事务控制 :经常提交 (COMMIT),以尽早释放锁,提高并发性。
  5. 减少连接:尽可能减少多表查询(Join)。

17.2.3、应用软件优化

数据库连接池 (Connection Pool):避免频繁创建和销毁数据库连接(握手开销大),实现连接复用。

17.3、分布式数据库优化

分布式环境下,优化的核心矛盾发生了转移:从"磁盘I/O"变成了"网络通信"

17.3.1、核心目标:降低通信代价

分布式查询的主要时间开销在数据在不同节点间的传输。

  • 全局查询树的变换:优化执行计划,减少跨节点数据传输。
  • 多副本策略:将数据复制到多个节点,实现"就近读取"。
  • 查询树的分解:将大查询拆解分发。

17.3.2、关键技术:半连接 (Semi-Join)

场景:表 A 在节点 1,表 B 在节点 2,要做 Join。

笨办法:把表 B 全传给节点 1 进行 Join。

半连接优化

  1. 节点 1 只把表 A 的 Join Key (连接键) 传给节点 2。
  2. 节点 2 筛选出匹配的行。
  3. 节点 2 只把匹配成功的数据传回节点 1。

效果:大幅减少了网络传输的数据量。

17.4、完整优化链条

数据库性能优化
集中式优化
分布式优化
硬件: CPU, 内存, I/O
系统: 参数调整
应用: 连接池
数据库设计 (重点)
表/物化视图
索引: 读多建, 写多避
SQL优化
不相关子查询
只查需要的列
用IN代替OR
尽早Commit释放锁
通信代价 (核心瓶颈)
全局查询树变换
多副本策略
半连接 Semi-Join

十八、NoSQL

18.1、核心概念与对比

NoSQL 泛指非关系型数据库 ,它的出现不是为了取代 SQL,而是为了解决传统关系型数据库在海量数据、高并发、多变结构场景下的瓶颈。

架构师视角:RDBMS 追求的是"规矩和准确",NoSQL 追求的是"速度和灵活"。

维度 关系型数据库 (RDBMS) NoSQL 数据库
应用领域 面向通用领域 (ERP, OA, 银行)。 特定应用领域 (社交、电商秒杀、日志)。
数据结构 结构化 (二维表),Schema 必须预定义。 非结构化 (Key-Value, 文档, 图),Schema 灵活。
扩展方式 向上扩展 (Scale-up):买更贵的服务器。 向外扩展 (Scale-out):加更多的普通机器 (集群)。
并发性能 支持并发,但性能受限于锁机制。 高并发,读写性能极高。
事务支持 高事务性 (ACID),强一致性。 弱事务性 (BASE),最终一致性。
数据量 有限数据 (TB级)。 海量数据 (PB级)。

18.2、NoSQL 的四大分类

分类 数据模型 典型代表 优缺点 适用场景
键值存储 (Key-Value) Map<Key, Value> 哈希表 Redis, Memcached :查找速度最快 (O(1)O(1)O(1))。 :数据无结构,只适合简单的存取。 内容缓存 (Session, 购物车)、高访问负载、日志系统。
列式存储 (Column-Family) 列簇为单位存储。 HBase, Cassandra :查找速度快,扩展性强,适合分布式。 :功能相对局限。 海量数据分布式存储 (如 Hadoop 生态)、分布式文件系统。
文档型 (Document) Key-Value 的变种。 Value 是 JSON/XML MongoDB, CouchDB :结构灵活 (Schema-free),表结构可变。 :缺乏统一查询语法。 Web应用 (日志, 评论, 用户画像),数据结构经常变化的业务。
图形数据库 (Graph) 图结构 (节点+边)。 Neo4J :擅长处理复杂关系 (如最短路径)。 :难以做分布式集群。 社交网络 (关注关系)、推荐系统、知识图谱。

18.3、NoSQL图谱

NoSQL 四大金刚
SQL vs NoSQL
RDBMS: 强一致, 结构化

垂直扩展
NoSQL: 高并发, 非结构

水平扩展
(1) 键值 Key-Value
(2) 文档 Document
(3) 列族 Column
(4) 图 Graph
Redis

缓存/秒杀
MongoDB

Web应用/用户画像
HBase

海量数据/日志分析
Neo4j

社交关系/推荐

18.4、决策逻辑

缓存层选型 :只要看到"提高读性能"、"Session共享"、"排行榜",首选 Redis (Key-Value)。

大数据存储选型 :只要看到"海量日志"、"PB级数据"、"Hadoop",首选 HBase (列式)。

灵活业务选型 :只要看到"字段不固定"、"快速迭代的互联网应用",首选 MongoDB (文档型)。

复杂关系选型 :只要看到"好友推荐"、"六度人脉",首选 Neo4J (图库)。

十九、联邦数据库系统 (FDBS)

19.1、核心定义

联邦数据库系统 (FDBS) :是一个彼此协作却又相互独立的成员数据库 (CDBS) 的集合。

成员数据库 (CDBS, Component Database System):构成联邦的各个子节点数据库。

联邦数据库管理系统 (FDBMS):对该系统整体提供控制和协同操作的软件。

本质:将成员数据库系统按不同程度进行集成,对外提供统一的访问接口,但内部各成员依然保持独立。

19.2、四大核心特征

19.2.1、分布性 (Distribution)

数据在物理上是分布在不同网络节点上的。

19.2.2、异构性 (Heterogeneity)

成员数据库可以是不同的类型(有的存文本文件 ,有的是 MySQL ,有的是 Oracle)。

硬件平台、操作系统、DBMS 都可以不同。

19.2.3、自治性 (Autonomy)

成员数据库虽然加入联邦,但依然保留对自己数据的控制权。它们可以独立运行,服务于原本的本地应用,不受联邦系统的完全支配。

19.2.4、透明性 (Transparency)

用户在使用联邦数据库时,感觉不到数据的分布、异构和底层实现细节,就像在使用一个集中式数据库一样。

19.3、分类

根据耦合程度的不同,FDBS 分为:

  • 紧耦合 (Tightly Coupled):有统一的全局模式,管理较严格。
  • 松耦合 (Loosely Coupled):没有统一的全局模式,各成员更自由,通常通过多数据库语言进行互操作。

19.4、FDBMS 统一管理底层异构数据源的架构

底层异构数据源 (CDBS)
统一查询
协同操作
协同操作
协同操作
协同操作
用户/应用程序
联邦数据库管理系统 FDBMS
MySQL
Oracle
文本文件
...

相关推荐
冉冰学姐2 小时前
SSM校园人才市场391d8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·开题报告·java 开发·ssm 框架应用
橘橙黄又青3 小时前
redis复习(2)
数据库·redis·缓存
sensen_kiss9 小时前
INT303 Big Data Analysis 大数据分析 Pt.12 推荐系统(Recommendation Systems)
大数据·数据挖掘·数据分析
wang_yb9 小时前
当条形图遇上极坐标:径向与圆形条形图的视觉革命
数据分析·databook
计算机毕设VX:Fegn08959 小时前
计算机毕业设计|基于springboot + vue医院设备管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
Mr__Miss9 小时前
保持redis和数据库一致性(双写一致性)
数据库·redis·spring
databook9 小时前
当条形图遇上极坐标:径向与圆形条形图的视觉革命
python·数据分析·数据可视化
Knight_AL10 小时前
Spring 事务传播行为 + 事务失效原因 + 传播行为为什么不用其他模式
数据库·sql·spring
倔强的石头_10 小时前
时序数据时代的“存储与分析困局”解析及金仓解决方案
数据库