数据库系统工程师-数据库

260 阅读33分钟

数据库理论基础

三级模式结构

数据抽象:

  • 视图层:描述整个数据库的某个部分。
  • 逻辑层:描述数据库中存储什么数据以及这些数据间存在什么关系。
  • 物理层:描述数据在存储器中是如何存储的。

三级模式:

  • 外模式:也称用户模式子模式,是用户与数据库系统的接口,是用户用到的那部分数据的描述。
  • 概念模式:也称模式,是数据库中全部数据的逻辑结构和特征的描述,只涉及型的描述,不涉及具体的值。“型”大致上对应 SQL 中的 DDL,也就是描述数据的逻辑关系,不涉及具体的数据记录。值会随着时间变化,但型不会。
  • 内模式:也称存储模式,是数据物理结构和存储方式的描述,定义所有的内部记录类型、索引和文件的组织方式,以及数据控制方面的细节。

两级映象:外模式/模式映像、模式/内模式映像

数据的独立性:

  1. 数据的物理独立性:它是指当数据库的内模式发生改变时,数据的逻辑结构不变。
  2. 数据的逻辑独立性:它是指用户的应用程序与数据库的逻辑结构是相互独立的,数据的逻辑结构发生变化后,用户程序也可以不修改,但是,为了程序能够正确执行,需要修改外模式/模式之间的映像。
graph TD
a[外模式]
b[模式/概念模式]
c[内模式]
a-->b
b-->c
d[视图层/视图]
e[逻辑层/基本表]
f[物理层/存储文件]
d-->e
e-->f

属性

  • 简单属性和复合属性:简单属性是原子的、不可再分的,复合属性可以细分为更小的部分。如通信地址可以进一步细分为省、市、街道、邮编等。
  • 单值属性和多值属性:指一个属性有单个值或多个值。如职工的亲属姓名就是多值属性(可以有多个取值)。
  • NULL属性:当实体在某个属性上没有值或属性值未知时,使用 NULL值。表示无意义或不知道。
  • 派生属性:可以从其他属性得来。如参加工作时间和工作年限,身份证号和年龄等。

基本概念

  • 关系:实体以及实体间的联系都是用关系来表示的。类似于程序设计语言中变量的概念。
  • 关系模式:是对关系的描述。类似于程序设计语言中类型定义的概念。例如: Student(Sno,Sname,SD,Sex)
  • 关系模型:是由若干个关系模式组成的集合。
  • 属性/字段:用来描述某一个事物的特征。可理解为表中的列,在关系模式Student(Sno,Sname,SD,Sex)中,Sno 就是一个属性。
  • 域:每个属性的取值范围所对应一个值的集合。
  • 候选码:若关系中的某一属性或属性组的值能唯一标识一个元组,则称该属性或属性组为候选码,也称为
  • 主码:又称为主键,若一个关系有多个候选码,则选定其中一个为主码。
  • 主属性:包含在任何候选码中的属性称为主属性。
  • 非主属性:不包含在任何候选码中的属性称为非主属性。
  • 外码:如果关系模式 R 中的属性或属性组非该关系的码,但它是其他关系的码,那么该属性集对关系模式 R 而言是外码。
  • 全码:关系模型的所有属性组是这个关系模式的候选码,或者说一个码包含了所有属性,没有非主属性,称为全码。全码也是码。
  • 元组/记录:行,可以理解为表中的一行数据
  • 元数:属性的个数(列数
  • 基数:记录的个数(行数
  • n 元关系:元数为几,就是几元关系,例如: Student(Sno,Sname,SD,Sex) 称为 4 元关系

关系模式

关系模式的完整表示是: R(U,D,dom,F)

其中 R 表示关系名;U 是组成该关系的属性名集合;D 是属性的域;dom 是属性向域的映像集合;F 为属性间数据的依赖关系集合。在描述一个关系时,通常不会给出完整定义,例如:

  • 学生关系模式: S(Sno,Sname,SD,SA)
  • 课程关系模式: C(Cno,Cname,PCno),Dom(PCno)=CnoCno 表示课程号,先修课程号是 PCnoDom(PCno)=Cno 表示 PCno 的值只能来源于 Cno
  • 集合 D1={0,1,2},表示关系 D1 只有一个列(第一列),该列的取值是 0、1、2
  • 集合 D2={(0,1,2), (3,4,5)},表示关系 D2 有三个列,两个元组

关系的三种类型

  • 基本关系(基本表或基表): 它是实际存在的表,是实际存储数据的逻辑表示。
  • 查询表: 查询结果对应的表。比如一条 select 语句的查询结果就是一个查询表。
  • 视图表: 它是一种虚拟表,是由基本表或其他视图表导出的表。它本身是不独立存储在数据库的,数据库只存放它的定义。

关系的完整性约束

是对关系的某种约束条件,用来保证用户对数据库作出修改时不会破坏数据的一致性,防止对数据的意外破坏。

  1. 实体完整性:是指基本关系 R 的主属性不能取空值
  2. 参照完整性:等同于外键关系,或者说可以通过外键来实现。例如这里关系模式 S 的 “所属院系” 就是一个外键
    • S(学号,姓名,所属院系,年龄)
    • D(院系编号,学院名称,学院地址,系主任)
  3. 用户定义完整性:是针对某一具体的关系数据库的约束条件,反映某一具体应用所涉及到的数据必须满足的语义要求。比如规定性别只能取 "男" 或者 "女"

关系运算

关系运算是以关系为基本单位的运算,或者说是对表进行运算。比如 "并" 就是合并两个表,得到一个新的表。

  • 五种基本的代数运算:并(∪)、差(-)、广义笛卡儿积(×)、投影(π)、选择(σ)。

    其中并、差和广义笛卡儿积都是对两个关系进行运算,而投影和选择是对一个关系进行运算。除此之外还有:交(∩)

  • 并运算、交运算和差运算都要求两个关系具有相同的关系模式

  • 投影记为 πA(R)π_A(R),投影指的是从关系中筛选出部分列;广义投影指的是可以在投影的同时,通过运算生成新的列。例如:πname,sex,age(Salary)π_{name,sex,age}(Salary)π工号,姓名,工资+资金(Salary)π_{工号,姓名,工资+资金}(Salary)

  • 选择记为 σF(R)σ_F(R),选择指的是从关系中筛选出部分行。例如:

    σ16(R)σ_{1≥6}(R) 表示选取关系 R 中第 1 个属性值大于等于第 6 个属性值的元组;

    σAB(R)σ_{A≥B}(R) 表示选取关系 R 中 A 属性值大于等于 B 属性值的元组;

    σ16(R)σ_{1≥'6'}(R) 表示选取关系 R 中第 1 个属性值大于等于数值 6 的元组;

  • 连接包括:

    • θ连接:RXθYSR{\bowtie \over {XθY}}S = σXθY(R×S)σ_{XθY}(R×S),表示 R 与 S 的笛卡儿积,并筛选出符合 XθY 条件的行,例如:RR.A<S.ASR{\bowtie \over {R.A<S.A}}S
    • 等值连接:RX=YSR{\bowtie \over {X=Y}}S = σX=Y(R×S)σ_{X=Y}(R×S),当θ连接的筛选条件使用等值比较时,称为等值连接
    • 自然连接:RSR{\bowtie}S,一种特殊的等值连接,它要求两个关系中进行比较的分量必须是相同的属性组,并且在结果集中将重复属性列去掉。如果没有重复属性,那么自然连接就转化为笛卡儿积。比如有两个关系:R(A,B,C) 和 S(A,C,D),则 RSR{\bowtie}S = RR.A=S.AR.C=S.CSR{\bowtie \over {R.A=S.A∧R.C=S.C}}S,注意,只要属性在两个关系中都出现,就必须参与比较。
    • 左外连接:取出左侧关系中所有与右侧关系中任一元组都不匹配的元组,用空值 NULL 填充所有来自右侧关系的属性,构成新的元组,将其加入自然连接的结果中。
    • 右外连接:取出右侧关系中所有与左侧关系中任一元组都不匹配的元组,用空值 NULL 填充所有来自左侧关系的属性,构成新的元组,将其加入自然连接的结果中。
    • 全外连接:将左外连接和右外连接的结果求并集

元组演算

元组演算与关系运算类似,可以在多个关系中,演算得到一个新的关系模式/表。在元组演算中,其元组演算表达式中的变量是以元组为单位的。

若一个公式的一个元组变量前有全称量词 ⩝ 或存在量词 ∃ 符号,则称该变量为约束变量,否则称之为自由变量。

  • R1 = { t | R(t) ∧ ¬S(t) } :

    表示关系 R1 由元组 t 组成,t 称为元组变量。同时 t 要满足 "R(t) ∧ ¬S(t)" 这个条件。其中 R(t) 表示 t 是关系 R 中的一个元组,¬ 表示“非”,所以 ¬S(t) 表示 t 不是关系 S 中的元组。

  • R2 = { t | S(t) ∧ t[3]>t[2] ∧ t[2]<8 } :

    表示元组来自 S,并且元组的第 3 个值大于第 2 个值,并且第 2 个值小于数值 8

  • R3 = { t | (∃u) (R(t) ∧ S(u) ∧ t[3]<u[2]) } :

    关系 R3 由元组 t 组成,t 来自于 R,元组 u 来自于 S(注意,这里的元组 u 只是用来演算的,不会出现在 R3 中),t[3]<u[2] 表示 t 的第 3 列的值小于 u 的第 2 列的值,是所有 u 吗?还是只要有满足条件的一个或多个 u 即可?这要看前面的 (∃u), 称为存在量词,表示只要至少有一个满足条件即可。

  • R4 = { t | (⩝u) (R(t) ∧ S(u) ∧ t[3]>u[1]) }:

    称为全称量词,表示要求所有的 u 都满足条件

    ∃t(φ1) 表示这样一个命题: “如果有一个 t 使 φ1 为真,则 ∃t(φ1) 为真,否则 ∃t(φ1) 为假”

    ⩝t(φ1) 表示这样一个命题: “如果所有的 t 使 φ1 为真,则 ⩝t(φ1) 为真,否则 ⩝t(φ1) 为假”

  • R5 = { t | (∃u) (∃v) (R(u) ∧ S(v) ∧ u[2]>v[1] ∧ t[1]=u[1] ∧ t[2]=v[1] ∧ t[3]=v[3]) }

    这个公式中没有指定 t 来自哪个关系,但指定了 t[1]=u[1] ∧ t[2]=v[1] ∧ t[3]=v[3],这就定义了 t 的所有列以及值的来源。

  • ⩝u(S(u)→t[3]>u[1]) : 元组演算中,φ1⇒φ2 表示 “若 φ1 为真则 φ2 为真”。这个表达式的含义是:所有 S 中的元组都要使得 t[3]>u[1] 为真

域演算

在域演算中,表达式中的变量是表示域的变量,可将关系的属性名视为域变量。

若一个公式的一个元组变量前有全称量词 ⩝ 或存在量词 ∃ 符号,则称该变量为约束变量,否则称之为自由变量。

  • R1={t1t2t3R(t1t2t3)t1<t2t2>t3}R1 = \{ t_1t_2t_3|R (t_1t_2t_3 ) ∧ t_1<t_2 ∧ t_2>t_3 \}:

    R(t1t2t3)R (t_1t_2t_3 ) 表示关系 R 中的各个列,而 R1 的列正是 t1t2t3t_1t_2t_3 ,所以 R1 中的元组来自关系 R,而且元组中,第一个值小于第二个值,第二个值大于第三个值

  • R2={t1t2t3(R(t1t2t3)t1>4)(S(t1t2t3)t2<8)}R2 = \{ t_1t_2t_3 | ( R (t_1t_2t_3 ) ∧ t_1>4 ) ∨ ( S (t_1t_2t_3 ) ∧ t_2<8) \}:

    元组可以来自于关系 R,但要求 t1 大于 4,元组也可以来自于关系 S,但要求 t2 小于 8

  • R3={t1t2t3(u)(v)(w)R(ut2v)S(t1wt3)u7v>w}R3 = \{ t_1t_2t_3 | (∃u)(∃v)(∃w) R(ut_2v) ∧ S(t_1wt_3 ) ∧ u⩾7 ∧ v>w \}:

    R中的各列为 ut2vut_2v,S中的各列为 t1wt3t_1wt_3 ,而 R3 的各列为 t1t2t3t_1t_2t_3 ,所以 R3 的列来自于 S 的第一列、R 的第二列、S 的第三列。

查询优化

查询优化是指为查询选择最有效的查询计划的过程。一个查询可能会有多种实现方法,关键是如何找出一个与之等价的且操作时间又少的表达式,以节省时间、空间,提高查询效率。

  • 在关系代数运算中,笛卡儿积、连接运算是最耗费时间和空间的。
  • 提早执行选取运算。对于有选择运算的表达式,应优化成尽可能先执行选择运算的等价表达式,以得到较小的中间结果,减少运算量和从外存读块的次数。
  • 合并乘积与其后的选择运算为连接运算,换句话说,要连接不要乘积,因为连接自带选择运算,效率更高。
  • 将投影运算与其后的其他运算同时进行,以避免重复扫描关系。
  • 将投影运算和其前后的二目运算结合起来,使得没有必要为去掉某些字段再扫描一遍关系。
  • 在执行连接前对关系适当地预处理,就能快速地找到要连接的元组。方法有两种:索引连接法、排序合并连接法。
  • 存储公共子表达式。对于有公共子表达式的结果应存于外存(中间结果),这样,当从外存读出它的时间比计算的时间少时,就可节约操作时间。

示例:

优化前:π学号,姓名(σ成绩>90性别=(SSC))π_{学号,姓名}(σ_{成绩>'90'∧性别='男'}(S \bowtie SC))

优化后:π学号,姓名(σ性别=(S))π学号(σ成绩>90(SC))π_{学号,姓名}(σ_{性别='男'}(S)) \bowtie π_{学号}(σ_{成绩>'90'}(SC))

π学号(σ成绩>90(SC))π_{学号}(σ_{成绩>'90'}(SC)) 这个优化中,投影运算只选取了连接所需的属性,选择运算使用到的属性,并不一定要包含在投影运算中。

语法树

语法树中,连接运算要转换为乘积运算、选择运算和投影运算。

Snipaste_2022-04-04_10-46-05.png

函数依赖

设 R(U) 是属性集 U 上的关系模式,X、Y 是 U 的子集。若对 R(U) 的任何一个可能的关系 r,r 中不可能存在两个元组在 X 上的属性值相等,而在 Y 上的属性值不等,则称 X 函数决定 Y 或 Y 函数依赖于 X,记作:X→Y。例如:

R(U) => 学生(学号,姓名,年龄,性别)
X =>    {学号}
Y =>    {姓名,年龄}

换句话说,随意两个 R 上的元组,只要 X 是相等的,那么 Y 就一定相等

  • 如果 X→Y,那么对于任意两个相同的 X,所对应的 Y 是一定相同的。
  • 如果 X→Y,但 Y⊈X,则称 X→Y 是非平凡的函数依赖。例如: {学号} → {姓名,年龄}。一般情况下总是讨论非平凡的函数依赖。
  • 如果 X→Y,但 Y⊆X,则称 X→Y 是平凡的函数依赖。例如: {学号,年龄} → {年龄}
  • 函数依赖的定义要求关系模式R的任何可能的r都满足上述条件。因此不能仅考察关系模式R在某一时刻的关系 r,就断定某函数依赖成立。
  • 函数依赖是语义范畴的概念,我们只能根据语义来确定函数依赖。

完全函数依赖与部分函数依赖

在 R(U) 中,如果 X→Y,并且对于 X 的任何一个真子集 X',都有 X' 不能决定 Y,则称 Y 对 X 完全函数依赖,记作:X→Y。如果 X→Y,但 Y 不完全函数依赖于 X,则称 Y 对 X 部分函数依赖,记作:XPYX {P \over →}Y。部分函数依赖也称局部函数依赖。

选课关系SC1:(学号,课程号,成绩),F={ (学号,课程号)→成绩 }
# 完全函数依赖

选课关系SC2:(学号,课程号,学生姓名,课程名称,成绩),F={ (学号,课程号)
→成绩,(学号,课程号)→课程名称 }
# 其中 “(学号,课程号)→ 课程名称” 是部分函数依赖,因为课程号就可以单独决定课程名称

候选码:设 K 为 R(U,F) 中的属性的组合,若 K→U,且对于 K 的任何一个真子集 K',都有 K' 不能决定 U,则 K 为 R 的候选码,也称为候选关键字。

外码:若 R(U) 中的属性或属性组 X 非 R 的码,但 X 是另一个关系的码,则称 X 是 R 的外码(Foreign Key)或称外键。

传递函数依赖

在 R(U,F) 中,如果 X→Y,Y→Z,Y⊈X,Y⇸X,则称 Z 对 X 传递依赖

1.
供应商: R(Sno,Sname,Status,City,Pno,Qty)
函数依赖集: F = {Sno→Sname, Sno→Status, Status→City, (Sno,Pno)→Qty}
其中的函数依赖 “Sno→Status, Status→City” 就是一个传递依赖

2.
(学号,课程号)→成绩,成绩→等级 # 这是一个传递依赖

多值依赖

若关系模式 R(U) 中,X,Y,Z 是 U 的子集,并且 Z=U-X-Y。当且仅当对 R(U) 的任何一个关系 r,给定一对 (x,z) 值,有一组 Y 的值,这组值仅仅决定于 x 值而与 z 值无关,则称 “Y多值依赖于X” 或 “X多值决定Y” 成立。记为: X→→Y

R(U) => 参考书目(课程,参考书,教师)
X    => 课程
Y    => 参考书
Z    => 教师
X→→Y => 课程 →→ 参考书
在这个关系中,参考书仅由课程决定,与老师无关。一个指定的课程就会有多个参考书与之对应,从函数的角度看,输入一个 X,可以输出多个 Y
  • 如果 Z=∅,称为平凡的多值依赖,也就是 U=K+A,且 K→→A
  • 如果 Z≠∅,称为非平凡的多值依赖,也就是 U=K+A+B,且 K→→A,K→→B,A 与 B 之间相互独立。第 4 范式消除的正是这种非平凡多值依赖

多值依赖具有如下 6 条性质:

  1. 多值依赖具有对称性。即若 X→→Y,且 Z=U-X-Y,则 X→→Z

    如果 U=X+Y+Z,若 X→→Y,则 X→→Z

  2. 多值依赖的传递性。即若 X→→Y,Y→→Z,则 X→→Z-Y

  3. 函数依赖可以看成是多值依赖的特殊情况。

  4. 若X→→Y,X→→Z,则X→→YZ。

  5. 若X→→Y,X→→Z,则X→→Y ⋂ Z。

  6. 若X→→Y,X→→Z,则X→→Z-Y

Armstrong公理系统

设关系模式 R(U,F),其中 U 为属性集,F 是 U 上的一组函数依赖,那么有如下推理规则:

  1. A1 自反律:若 Y⊆X⊆U,则 X→Y 为 F 所蕴涵

    Y 是 X 的子集,所以 X 能函数决定 Y,例如 AB → A

  2. A2 增广律:若 X→Y 为 F 所蕴涵,且 Z⊆U,则 XZ→YZ 为 F 所蕴涵

    如果 X→Y,则 XZ→YZ

  3. A3 传递律:若 X→Y,Y→Z 为 F 所蕴涵,则 X→Z 为 F 所蕴涵

    如果 X→YY→Z ,则 X→Z

根据上述三条推理规则又可推出下述三条推理规则:

  1. 合并规则:若 X→YX→Z,则 X→YZ 为 F 所蕴涵

  2. 伪传递律:若 X→YWY→Z,则 XW→Z 为 F 所蕴涵

    X→Y,所以 WX→WY

  3. 分解规则:若 X→Y,Z⊆Y,则 X→Z 为 F 所蕴涵

    A→BC,则 A→B

函数依赖的闭包

关系模式 R(U,F) 中为 F 所逻辑蕴含的函数依赖的全体称为 F 的闭 包,记为:F+F^+

如果有关系 R(U,F),U =(A,B,C,D),F = {A→B, B→C, AC→D},那么 F+F^+ = {A→B, B→C, AC→D, A→A, B→B, C→C, AB→A, AB→B, BC→B, ...}

属性的闭包

设 F 为属性集 U 上的一组函数依赖,X⊆U,XF+X_{F^+} = { A | X→A 能由 F 根据 Armstrong 公理导出 },则称 XF+X_{F^+} 为属性集 X 关于函数依赖集 F 的闭包。

即:属性集 X 的闭包 XF+X_{F^+} 是指所有能由 X 决定的属性集合

例如:关系模式 R( U,F ),U = (A,B,C,D),F = { A→B,B→C,AC→D },则 AF+A_{F^+} = { A,B,C,D }

候选码的求解

给定一个关系模式 R(U,F), U={A1 ,A2 ,…,An },F 是 R 的函数依赖集,那么,可以将属性分为如下四类:

  • L:仅出现在函数依赖集 F 左部的属性
  • R:仅出现在函数依赖集 F 右部的属性
  • LR:在函数依赖集 F 左右部都出现的属性
  • NLR:在函数依赖集 F 左右部都未出现的属性

根据候选码的特性,对于给定一个关系模式 R(U,F),可以得出如下结论:

  • 结论1:若 X(X⊆U) 是 L 类属性,则 X 必为 R 的任一候选码的成员。若 XF+X_{F^+}=U,则 X 必为 R 的唯一候选码。
  • 结论2:若 X(X⊆U) 是 R 类属性,则 X 不是 R 的任一候选码的成员。
  • 结论3:是 X(X⊆U) 是 NLR 类属性,则 X 必为 R 的任一候选码的成员。
  • 结论4:若 X(X⊆U) 是 L 类和 NLR 类属性组成的属性集,若 XF+X_{F^+}=U,则 X 必为 R 的唯一候选码。

求解步骤

  1. 第1步、根据题意,将所有的属性分类:
    • L:只在左边出现,一定是
    • R:只在右边出现,一定不是
    • LR:左右都出现,有可能是,也有可能不是
    • NLR:左右都没出现,一定是
  2. 第2步、将所有的 L 类和 NLR 类属性组合起来,设为 P,求其闭包 PF+P_{F^+},如果是全集 U,那么它就是候选码。
  3. 第3步、如果 PF+P_{F^+} 不是全集 U,则依次将 LR 类属性跟 P 组合起来求闭包,只要其闭包是全集 U,就是候选码。

最小函数依赖集

如果函数依赖集 F 满足下列条件,则称 F 为一个最小函数依赖集,或称极小函数依赖集或最小覆盖。

  1. F 中的任一函数依赖的右部仅有一个属性,即无多余的属性;
  2. F 中不存在这样的函数依赖 X→A,使得 F 与 F-{X→A} 等价,即无多余的函数依赖;
  3. F 中不存在这样的函数依赖 X→A,X 有真子集 Z 使得 F 与 F-{X→A}⋃{Z→A} 等价,即去掉各函数依赖左边的多余属性。

即:

  1. 所有函数依赖的右侧只有一个属性。
  2. 没有冗余的函数依赖。
  3. 所有函数依赖的左侧没有冗余的属性。

例:关系模式 R(U, F),R(A, B, C, D, E),F={A→BC, A→E, A→D, D→E, AC→D},F 不是最小函数依赖集,其中:

  1. A→BC 中,右侧不只一个属性
  2. 由于 A→D,D→E,所以 A→D 是冗余的函数依赖
  3. 由于 A→D,所以 AC→D 中左侧的 C 是冗余的属性,也是冗余的函数依赖

无损连接

将一个关系模式分解成若干个关系模式后,通过自然连接和投影等运算仍能还原到原来的关系模式,则称这种分解为无损连接分解。

定理:关系模式 R(U,F) 的一个分解 ρ ={ R1(U1 ,F1), R2(U2 ,F2) },具有无损连接的充分必要的条件是:

U1⋂U2 → U1-U2 ∈ F+F^+ 或 U1⋂U2 → U2-U1 ∈ F+F^+

也就是说,分解后的两个关系模式,只要他们属性集的“交”(U1⋂U2)能函数决定“差”(不管是 U1-U2 还是 U2-U1),就是无损连接分解

注意:这个定理只适用于分解为两个子模式的情况,分解为多个子模式的时候不适用。

示例

对给定的关系模式 R(U,F),U={A,B,C},F={A→B}。

若分解为 ρ1 = {AB ,BC},意味着 R 被分解为 R1(A, B), R2(B, C),同时: U1⋂U2 => B U1-U2 => A U2-U1 => C 但是,BAF+B→A \notin F^+,而且 BCF+B→C \notin F^+,所以这种分解是有损

若分解为 ρ2 = {AB ,AC},意味着 R 被分解为 R1(A, B), R2(A, C),同时: U1⋂U2 => A U1-U2 => B U2-U1 => C 虽然 ACF+A→C \notin F^+,但是 ABF+A→B \in F^+,所以这种分解是无损

保持函数依赖

假设有关系 R( A1,A2,A3 ) ,函数依赖集 F = { A1A3→A2 , A1A2→A3 },R 上的一个分解为 ρ = { (A1,A2), (A1,A3) },也就是说,R 分解为 R1(A1, A2)、R2(A1, A3),在分解后的这两个关系中,都无法再实现函数依赖 A1A3→A2 或 A1A2→A3,因为要实现 A1A3→A2 必须同时具有 A1A2A3 三个属性,A1A2→A3 也一样。

这种情况称为没有保持函数依赖。

假设有关系 R( A1,A2,A3,A4 ),函数依赖集 F = { A1A3→A2,A2→A3 },R 上的一个分解为 ρ = { (A1,A2,A3), (A2,A3) },这种情况称为保持了函数依赖。

联系向关系模式的转换

一对一联系(1:1)

比如班主任与班级之间的任职关系就是一种一对一联系:

graph LR
a[班主任]
b{任职}
c[班级]
a -- 1 ------ b
b -- 1 ------ c

aa((工号))
ab((名称))

ba((任职时间))

ca((班级编号))
cb((名称))
cc((人数))

a---aa
a---ab
b---ba
c---ca
c---cb
c---cc

有以下两种方式可以将“联系”转换到关系模式中:

  1. 将联系转换成一个独立的关系模式(任职),属性包括该联系所关联的两个实体的码及联系的属性,关系的码取自任一方实体的码(一对一关系,码也是一对一,所以任一方都可以)。

    任职班主任工号,班级编号,任职时间)

  2. 将联系归并到关联的两个实体的任意一方,给待归并的一方实体属性集中增加另一方实体的码和该联系的属性即可,归并后的实体码保持不变。

    班主任(工号,姓名,班级编号,任职时间

    或:

    班级(班级编号,名称,人数,班主任编号,任职时间

一对多联系(1:*)

比如公司与员工之间的任职关系就是一种一对多联系:

graph LR
a[公司]
b{任职}
c[员工]
a -- 1 ------ b
b -- * ------ c

aa((编号))
ab((名称))

ba((入职时间))
bb((离职时间))

ca((工号))
cb((姓名))
cc((身份证号))

a---aa
a---ab
b---ba
b---bb
c---ca
c---cb
c---cc

有以下两种方式可以将“联系”转换到关系模式中:

  1. 将联系转换成一个独立的关系模式,属性取该联系所关联的两个实体的码及联系的属性,关系的码是多方实体的码。

    任职员工工号,公司编号,入职时间,离职时间)

  2. 将联系归并到关联的两个实体的多方,给待归并的多方实体属性集中增加一方实体的码和该联系的属性即可,归并后的多方实体码保持不变。

    员工(工号,姓名,身份证号,公司编号,入职时间,离职时间

多对多联系的转换(*:*)

比如学生与课程之间的选修关系就是一种多对多联系:

graph LR
a[学生]
b{选修}
c[课程]
a -- * ------ b
b -- * ------ c

aa((学号))
ab((姓名))

ba((成绩))
bb((出勤率))

ca((课程编号))
cb((课程名称))

a---aa
a---ab
b---ba
b---bb
c---ca
c---cb

多对多联系只能转换成一个独立的关系模式,属性取该联系所关联的两个多方实体的码及联系的属性,关系的码是多方实体的码构成的属性组。

选修(学号,课程编号,成绩,出勤率)

注:三方联系的多对多对多(:*)也是一样,只能转换成一个独立的关系模式,如:一个供应商可以给多个项目供应多种零件,一个项目可以使用多个供应商供应的多种零件。

供应关系(供应商编号,项目编号,零件号,数量)

事务

事务的特性

事务具 4 个特性:原子性、一致性、隔离性和持久性。

  • 原子性:事务的所有操作在数据库中要么都做要么都不做
  • 一致性:一个事务独立执行的结果,将保持数据的一致性,即数据不会因为事务的执行而遭受破坏。比如在转账前和转账后,账目总值是一致的。如果转出方数目已减,但转入方数目还未增加,则出现了不一致状态。
  • 隔离性:一个事务的执行不能被其他事务干扰。并发事务在执行过程中可能会对同一数据进行操作,这些事务的操作应该不会相互干扰,是相互隔离的。
  • 持久性:一个事务一旦提交,它对数据库的改变必须是永久的,即使系统出现故障也是如此。

事务的状态

事务是数据库的基本执行单元,如果事务成功执行,则数据库从一个一致状态进入另一个一致状态。如果因为某种原因事务没能成功执行,但其已经对数据库进行了修改,这时候可能会导致数据库处于不一致的状态,需要对事务已经造成的变更进行撤销(回滚)。

  1. 活动状态:事务的初始状态,事务执行时处于这个状态。
  2. 部分提交状态:当操作序列的最后一条语句执行后,事务就处于部分提交状态。这时,事务虽然已经完全执行,但由于实际输出可能还临时驻留在内存中,在事务成功完成前还有可能出现硬件故障,因此,部分提交状态并不等于事务成功执行。
  3. 失败状态:由于硬件或逻辑错误,使得事务不能继续正常执行,事务就进入了失败状态,处于失败状态的事务必须回滚。这样,事务就进入了中止状态。
  4. 中止状态:事务回滚并且数据库恢复到事务开始执行前的状态。
  5. 提交状态:当事务成功完成后,称事务处于提交状态。只有事务处于提交状态后,才能说事务已经提交。
graph LR
o((开始事务))
a((活动状态))
b((部分提交))
c((提交状态))
d((失败状态))
e((中止状态))
o -- begin transaction --> a
a -- end transaction --> b
b -- commit --> c
a -- abort --> d
d -- rollback --> e
  • BEGIN TRANSACTION:开始运行事务,使事务进入活动状态
  • END TRANSACTION:说明事物中的所有读写操作都已完成,使事务进入部分提交状态,把事务的所有操作对数据库的影响存入数据库。
  • COMMIT:标志事务已经成功地完成,事务中的所有操作对数据库的影响已经安全地存入数据库,事务进入提交状态,结束事务的运行。
  • ABORT:标志事务进入失败状态,系统撤销事务中所有操作对数据库和其他事务的影响,结束事务的运行。

事务调度

串行调度:是指多个事务依次串行执行,只有当一个事务的所有操作都执行完成后才执行另一个事务。

并发调度:利用分时的方法同时处理多个事务。也就是多个事务穿插着执行。并发调度时,可能会发生错误的调度。如果事务在并发调度后,数据库的状态与串行调度(任意一种串行调度)的状态相同,则是正确的调度。

可恢复调度:当事务 TjT_j 要读事务 TiT_i 写的数据时, TiT_i 事务先于事务 TjT_j 提交。

并发

并发操作带来的问题

并发操作带来的数据不一致性有以下几类:

  • 丢失修改:**【读了另一事务修改前的值,读一次】**两个事务对同一个数据进行修改,导致事务 A 对数据库的修改被事务 B 的修改所覆盖。比如两个事务都对某个数加 1,事务 T1 将 A 改为 A+1,事务 T2 也将 A 改为 A+1,最终 A 的值为 A+1 而不是 A+2。正确的做法是 T2 在 T1 执行完修改后再读取 A 的值。
  • 不可重复读:**【读两次,期间有另一事务修改数据】**事务对同一数据进行两次读取的结果不同。原因是两次读取的间隙数据被另一事务修改了。
  • 读脏数据:**【读了另一事务修改后的值,读一次,但修改被撤销了】**某事务读取的数据是其它事务修改后的值,但该修改后来又被撤销了。
  • 幻读:**【读两次,期间有另一事务对数据进行了增删】**事务 A 查询得到 N 条数据,然后事务 B 又插入了 M 条数据,或者改变了这 N 条数据之外的 M 条符合事务 A 搜索条件的数据,导致事务 A 再次搜索发现有 N+M 条数据了,这就产生了幻读,与不可重复读的区别是,不可重复读指的是同一组数据在不同时刻读出不同的值,幻读是指不同时刻能读出不同数量的数据。

并发调度的可串行性

  • 多个事务的并发执行时,当且仅当其结果与某一次序串行地执行它们的结果相同时才是正确的,称这种调度策略是可串行化的调度。
  • 可串行性是并发事务正确性的准则。即:一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度。

并发控制技术

并发事务如果对数据读写时不加以控制,会破坏事务的隔离性和一致性。为了保持事务的隔离性,系统必须对事务之间的相互作用加以控制,最典型的方式就是加锁。

  • 排它锁(Exclusive Locks,简称 X 锁):也称为写锁,用于对数据进行写操作时进行锁定。如果事务 T 对数据 A 加上 X 锁后,就只允许事务 T 对数据 A 进行读取和修改,其他事务对数据 A 不能再加任何锁,也不能读取和修改数据 A,直到事务 T 释放 A 上的锁。排它锁让事务完全独占数据。
  • 共享锁(Share Locks,简称 S 锁):也称为读锁,用于对数据进行读操作时进行锁定。如果事务 T 对数据 A 加上了 S 锁后,事务 T 就只能读数据 A 但不可以修改,其他事务可以再对数据 A 加 S 锁来读取,只要数据 A 上有了 S 锁,任何事务都只能再对其加 S 锁读取而不能加 X 锁修改。

封锁协议

  1. 一级封锁协议:是指事务 T 在修改数据 A 之前必须先对其加 X 锁,直到事务结束才释放 X 锁。解决了丢失修改的问题。
  2. 二级封锁协议:是一级封锁协议加上事务 T 在读取数据 A 之前必须对其加上 S 锁,读完后即可释放 S 锁。解决了读脏数据的问题。
  3. 三级封锁协议:是一级封锁协议加上事务 T 在读取数据 A 之前必须对其加上 S 锁,直到事务结束才释放 S 锁。解决了不可重复读的问题。

两段锁协议(2PL)

同一事务对任何数据进行读写之前必须对该数据加锁;在释放一个封锁之后,该事务不再申请和获得任何其他封锁。

两段指的是:事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段;第二阶段是释放封锁,也称为收缩阶段。

如果事务遵循两段锁协议,那么它们的并发调度是可串行化的。两段锁是可串行化的充分条件,但不是必要条件。即:遵循两段锁协议,一定是可串行化的,不遵循两段锁协议,可能是可串行化的,也可能不是。

多个事务的并发执行是正确的,当且仅当其结果与某一次序串行地执行它们的结果相同,称这种调度策略是可串行化的调度

可串行性是并发事务正确性的准则。即:一个给定的并发调度,当且仅当它是可串行化的才认为是正确调度。

采用两段锁协议也有可能产生死锁,死锁的处理策略包括:鸵鸟策略、预防策略、避免策略和检测与解除死锁。

数据库的备份与恢复

数据库系统故障的种类

  • 事务故障:是由于程序执行错误而引起事务非预期的、异常终止的故障。通常有如下两类错误引起事务执行失败:
    1. 逻辑错误。如非法输入、找不到数据、溢出、超出资源限制等原因引起的事务执行失败。
    2. 系统错误。系统进入一种不良状态(如死锁),导致事务无法继续执行。
  • 系统故障:是指硬件故障软件(如DBMS、OS或应用程序)漏洞的影响,导致丢失了内存中的信息,影响正在执行的事务,但未破坏存储在外存上的信息。
  • 介质故障:是指数据库的存储介质发生故障,如磁盘损坏、瞬间强磁场干扰等。这种故障直接破坏了数据库,会影响到所有正在读取这部分数据的事务。

数据库备份

数据转储是将数据库自制到另一个磁盘或磁带上保存起来的过程,又称为数据备份。

  1. 静态转储动态转储。静态转储是指在转储期间不允许对数据库进行任何存取、修改操作;动态转储是在转储期间允许对数据库进行存取、修改操作,因此,转储和用户事务可并发执行。
  2. 海量转储增量转储。海量转储是指每次转储全部数据;增量转储是指每次只转储上次转储后更新过的数据。
  3. 日志文件。在事务处理的过程中,DBMS 把事务开始、事务结束以及对数据库的插入、删除和修改的每一次操作写入日志文件。
  4. 数据库镜像。为了避免磁盘介质出现故障影响数据库的可用性,许多 DBMS 提供数据库镜像功能用于数据库恢复。

数据库恢复

要使数据库在发生故障后能够恢复,必须建立冗余数据,在故障发生后利用这些冗余数据实施数据库恢复,常用的是数据转储和日志文件。

故障恢复的两个操作

  1. 撤销事务(UNDO):

    将未完成的事务撤销,使数据库恢复到事务执行前的正确状态。

    撤销事务的过程:反向扫描日志文件(由后向前扫描),查找事务的更新操作;对该事务的更新操作执行逆操作,用日志文件记录中更新前的值写入数据库,插入的记录从数据库中删除,删除的记录重新插入数据库中;继续反向扫描日志文件,查找该事务的其它更新操作并执行逆操作直至事务开始标志。

  2. 重做事务(REDO):

    将已提交的事务重新执行。

    重做事务的过程:从事务的开始标志起,正向扫描日志文件,重新执行日志文件登记的该事务对数据库的所有操作,直至事务结束标识。

故障恢复策略

  • 事务故障的恢复:事务故障是事务在运行至正常终止点(SUMMIT 或 ROLLBACK)前终止,日志文件只有该事务的开始标识而没有结束标识。对这类故障的恢复通常是通过**撤销(UNDO)**产生故障的事务,使数据库恢复到该事务执行前的正确状态来完成的。

    具体做法:

    1. 反向扫描日志文件,查找该事务的更新操作。

    2. 对事务的更新操作执行逆操作。

    3. 继续反向扫描日志文件,查找该事务的其他更新操作,并做同样的处理,直到事务的开始标志。

    注:事务故障的恢复是由系统自动完成的,对用户是透明的。

  • 系统故障的恢复:系统故障会使数据库的数据不一致:

    1. 未完成的事务对数据库的更新可能已经写入数据库;

    2. 已提交的事务对数据库的更新可能还在缓冲区没来得及写入数据库。

    因此对于系统故障,恢复操作是 UNDO+REDO

    1. 撤销故障发生时未完成的事务(UNDO)。

    2. 重做已经提交的事务(REDO)。

  • 介质故障的恢复:介质故障时数据库遭到破坏,需要重装数据库,一般需要 DBA 的参与,装载故障前最近一次的备份和故障前的日志文件副本,再按照系统故障的恢复过程执行**撤销(UNDO)和重做(REDO)**来恢复。

检查点机制(CHECKPOINT)

在日志中设置检查点,当发生故障需要利用日志文件恢复时,反向扫描日志文件,找到检查点,确认检查点时刻正在执行的事务(活动事务),即检查点前有事务开始标志但没有事务结束标志。

  • 对于检查点后提交的事务,执行 REDO(重做)

    能检索到提交操作,说明事务是完整的,可以重做

  • 对于检查点后未提交的事务,执行 UNDO(撤销)

    没检索到提交操作,说明事务不完整,无法重做,只能撤销