全部文章

Database Principle

文章目录

2024-2025 DCS281

本文基于《Database System Concepts, 7th Ed.》的课本内容,对 SYSU-CSE DCS281 计算机专业课《数据库系统原理》进行较为详尽的知识点总结,涵盖少量实例分析。

第一章 数据库系统概述

1.1 数据库应用实例

重点:

难点:

实例分析:

1.2 数据与数据库系统

重点:

难点:

实例分析:

1.3 数据库系统组件

重点:

难点:

实例分析:

1.4 数据库设计

重点:

难点:

实例分析:

1.5 数据定义语言(DDL)与数据操作语言(DML)

重点:

难点:

实例分析:

1.6 SQL查询语言

重点:

难点:

实例分析:

1.7 数据库系统架构

重点:

难点:

实例分析:

1.8 数据库系统的发展历史

重点:

难点:

实例分析:


第二章 关系模型与关系代数

2.1 关系模型基础

重点:

难点:

实例分析:

2.2 关系的性质

重点:

难点:

实例分析:

2.3 关键字概念

重点:

难点:

实例分析:

2.4 关系代数操作

重点:

难点:

实例分析:

2.5 关系操作实例

重点:

难点:

实例分析:

2.6 关系代数运算示例解析

重点:

难点:

实例分析:


2.7 重点与难点总结

重点:

  1. 关系模型的基本概念:关系、属性、元组、主键与外键。
  2. 关系代数的基本操作:选择、投影、并、差、笛卡尔积、重命名。
  3. 数据库设计:逻辑设计与物理设计,模式与实例的区别。
  4. SQL语言:DDL与DML的使用,编写复杂查询。
  5. 数据库系统架构与组件:存储管理器、查询处理器、事务管理。

难点:

  1. 关系代数的复杂操作:多步操作的组合理解与应用。
  2. 数据库设计中的范式:确保数据库设计符合高范式以避免数据冗余。
  3. 事务管理与ACID特性:理解事务的原子性、一致性、隔离性与持久性。
  4. SQL的高级查询:子查询、联接操作、优化查询性能。

2.8 关键术语解释


2.9 图表与图示描述

由于文本性质,以下为关键图表和图示的文字描述:

  1. 数据库系统架构图:

    • 分为三个层次:
      • 物理层:描述数据在存储介质上的存储方式。
      • 逻辑层:描述数据库中的数据结构和关系。
      • 视图层:不同用户视角的数据展示,隐藏底层细节。
  2. 关系模型示意图:

    • 展示一个关系的二维表结构,包括属性名、元组行,以及主键与外键的标识。
  3. 关系代数操作流程图:

    • 例如,选择操作σ的流程:输入关系 → 应用选择条件 → 输出满足条件的子集关系。
  4. 事务管理流程图:

    • 展示事务的开始、执行、提交或回滚的过程,确保ACID特性。

2.10 实例分析详解

实例1:关系代数的选择操作

问题描述:

σ dept_name="Physics" AND salary > 90000 (instructor)

步骤解析:

  1. 输入关系:instructor表。
  2. 选择条件:dept_name等于”Physics”且salary大于90000。
  3. 执行操作:遍历instructor表中的每个元组,筛选符合条件的元组。
  4. 输出结果:所有属于”Physics”系且工资超过90000的教员信息。

SQL等效语句:

SELECT *
FROM instructor
WHERE dept_name = 'Physics' AND salary > 90000;

实例2:关系代数的联合操作

问题描述:
查找所有在2017年秋季和2018年春季教授的课程。

关系代数表达式:

π course_id (σ semester="Fall" AND year=2017 (section))
∪
π course_id (σ semester="Spring" AND year=2018 (section))

步骤解析:

  1. 第一部分:选择semester为”Fall”且year为2017的section表中的course_id。
  2. 第二部分:选择semester为”Spring”且year为2018的section表中的course_id。
  3. 执行联合操作:将两部分结果合并,去除重复的course_id。
  4. 输出结果:所有在指定学期教授过的课程编号。

SQL等效语句:

SELECT course_id
FROM section
WHERE semester = 'Fall' AND year = 2017
UNION
SELECT course_id
FROM section
WHERE semester = 'Spring' AND year = 2018;

实例3:SQL插入操作

问题描述:
向student表中插入一个新学生,学号为4,姓名“小周”,性别“女”。

关系代数表达式:

S ← S ∪ { (4, "小周", "女") }

SQL语句:

INSERT INTO student (student_no, name, sex) VALUES (4, '小周', '女');

执行步骤:

  1. 指定目标关系:student表。
  2. 定义新元组:学号为4,姓名“小周”,性别“女”。
  3. 执行插入操作:将新元组添加到student表中。

结果:
student表新增一条记录:

student_no | name | sex
---------- | ---- | ---
4 | 小周 | 女

实例4:关系代数的笛卡尔积与选择

问题描述:
将instructor与teaches表进行笛卡尔积,并筛选出匹配的教员与课程。

关系代数表达式:

σ instructor.id = teaches.id (instructor × teaches)

步骤解析:

  1. 笛卡尔积:生成instructor与teaches表的所有可能行组合。
  2. 选择操作:仅保留instructor.id等于teaches.id的元组。
  3. 输出结果:教员与其教授课程的对应关系。

SQL等效语句:

SELECT *
FROM instructor
JOIN teaches ON instructor.id = teaches.id;

结果示例:

instructor.ID | instructor.name | teaches.course_id
------------- | --------------- | -----------------
1 | 张三 | CS101
2 | 李四 | CS102

第三章 SQL查询语言概述

3.1 重点

  1. SQL语言的组成:

    • 数据定义语言(DDL):包括创建、修改和删除数据库对象(如表、视图、索引等)。
    • 数据操纵语言(DML):用于查询、插入、更新和删除数据。
    • 数据控制语言(DCL):用于授权和管理用户权限。
  2. SQL数据类型与模式:

    • 常用数据类型包括char(n)、varchar(n)、int、smallint、numeric(p,d)、real、double precision、float(n)等。
    • 模式定义了数据库中各关系的结构,包括属性名、数据类型、完整性约束等。
  3. SQL语句结构:

    • SELECT语句:包含SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY等子句。
    • JOIN操作:包括自然连接、内连接和外连接,理解不同类型的连接及其应用。
  4. 完整性约束:

    • 实体完整性:主键不允许为NULL,确保每个元组的唯一性。
    • 参照完整性:外键必须引用另一个关系的主键或唯一键。
    • 用户定义完整性:通过CHECK约束等自定义规则确保数据的合理性。

3.2 难点

  1. 子查询与嵌套查询:理解相关子查询与非相关子查询的区别及其在SELECT、WHERE、FROM子句中的应用。
  2. 复杂连接操作:掌握多表连接、不同类型的外连接及其在实际问题中的应用,如避免错误的自然连接导致的数据失配。
  3. 聚集函数与分组:正确使用GROUP BY和HAVING子句,理解聚集函数的作用及其在查询优化中的重要性。
  4. 视图的创建与管理:理解视图的本质、创建复杂视图的条件以及视图的更新限制。

3.3 实例分析

例1:查找所有导师的姓名

SELECT name
FROM instructor;

解答:
该查询语句选择instructor关系中的name属性,返回所有导师的姓名。


例2:查找计算机科学系的导师姓名和课程编号

SELECT name, course_id
FROM instructor, teaches
WHERE instructor.ID = teaches.ID
AND instructor.dept_name = 'Comp. Sci.';

解答:
该查询通过连接instructor和teaches关系,筛选计算机科学系的导师,并返回他们的姓名及所教授的课程编号。


例3:删除数学成绩不及格的学生选修信息

DELETE FROM takes
WHERE grade < '60';

解答:
该语句从takes关系中删除所有成绩低于60的记录,假设60为及格线。


例4:增加所有导师的薪水5%

UPDATE instructor
SET salary = salary * 1.05;

解答:
该语句将instructor关系中所有导师的薪水增加5%。


例5:使用子查询查找平均薪水以上的导师

SELECT name
FROM instructor
WHERE salary > (SELECT AVG(salary) FROM instructor);

解答:
该查询返回薪水高于所有导师平均薪水的导师姓名。


例6:创建视图显示不包含薪水的导师信息

CREATE VIEW faculty AS
SELECT ID, name, dept_name
FROM instructor;

解答:
该视图faculty仅包含导师的ID、name和dept_name,隐藏了salary属性,提高了数据的安全性。


例7:使用JOIN操作查找学生及其班主任

假设有两个关系:

SELECT Stu.name, Class.ban_zhuren
FROM Stu
JOIN Class ON Stu.ban_hao = Class.ban_hao;

解答:
该查询通过JOIN操作连接Stu和Class关系,返回每个学生的姓名及其对应的班主任。


第四章 访问SQL与触发器

4.1 重点

  1. 访问SQL的两种方法:

    • 动态SQL:在运行时生成和执行SQL语句,适合需要根据条件动态改变查询的场景。
    • 嵌入式SQL:将SQL语句嵌入到一般编程语言中,编译时转换为函数调用,提高执行效率。
  2. JDBC(Java数据库连接):

    • 核心操作流程:加载驱动、建立连接、创建语句、执行查询、处理结果、关闭连接。
    • 特点:跨平台、支持对象-关系映射、网络独立性、数据库独立性等。
  3. 完整性约束与事务管理:

    • 事务的ACID特性:原子性、一致性、隔离性、持久性。
    • 事务控制语句:BEGIN TRANSACTION、COMMIT、ROLLBACK等。
  4. 触发器:

    • 定义与作用:在特定事件(如INSERT、UPDATE、DELETE)发生时自动执行的存储过程。
    • 类型:AFTER触发器、INSTEAD OF触发器。
    • 应用实例:维护数据一致性、自动更新相关表、日志记录等。

4.2 难点

  1. 触发器的设计与实现:如何正确设计触发器的触发条件和执行动作,避免递归触发和性能问题。
  2. 事务的并发控制与隔离级别:理解不同隔离级别(如读未提交、读已提交、可重复读、串行化)的特点及其对数据库性能与一致性的影响。
  3. JDBC的高级特性:如批处理、预编译语句、元数据获取等,需要深入理解其API的使用方法和最佳实践。
  4. 权限管理与安全性:正确配置用户权限,理解角色的创建与分配,以及视图在数据安全中的应用。

4.3 实例分析

例1:使用JDBC连接数据库并查询导师信息

public static void JDBCexample(String dbid, String userid, String passwd) {
try (Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:@db.yale.edu:2000:univdb", userid, passwd);
Statement stmt = conn.createStatement()) {
// 执行查询
ResultSet rs = stmt.executeQuery("SELECT * FROM instructor");
while (rs.next()) {
System.out.println(rs.getString("name") + " " + rs.getFloat("salary"));
}
} catch (SQLException sqle) {
System.out.println("SQLException : " + sqle);
}
}

解答:
该Java方法使用JDBC连接到Oracle数据库,查询instructor关系中的所有记录,并输出导师姓名和薪水。使用了try-with-resources语法确保资源自动关闭。


例2:创建一个AFTER触发器,更新学生总学分

CREATE TRIGGER credits_earned
AFTER UPDATE OF takes.grade
ON takes
REFERENCING NEW ROW AS nrow OLD ROW AS orow
FOR EACH ROW
WHEN (nrow.grade <> 'F' AND nrow.grade IS NOT NULL AND (orow.grade = 'F' OR orow.grade IS NULL))
BEGIN ATOMIC
UPDATE student
SET tot_cred = tot_cred +
(SELECT credits FROM course WHERE course.course_id = nrow.course_id)
WHERE student.id = nrow.id;
END;

解答:
该触发器在takes关系的grade属性更新后执行。当新成绩不为F且不为空,且原成绩为F或为空时,触发器将相应课程的学分加到学生的总学分tot_cred中,确保学生的学分信息实时更新。


例3:使用JDBC执行INSERT操作并处理潜在的SQL注入风险

String sql = "INSERT INTO instructor (ID, name, dept_name, salary) VALUES (?, ?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "88877");
pstmt.setString(2, "Perry");
pstmt.setString(3, "Finance");
pstmt.setInt(4, 125000);
pstmt.executeUpdate();

解答:
该Java代码使用预编译语句PreparedStatement插入新导师记录,避免了通过字符串拼接可能引发的SQL注入风险。确保了数据的安全性和完整性。

4.4 第三章与第四章中提及的重要图示描述

  1. 视图的依赖关系图:

    • 说明:图中展示了视图之间的依赖关系,如视图physics_fall_2017_watson依赖于视图physics_fall_2017,而physics_fall_2017又依赖于基础关系course和section。
    • 关键点:展示了视图层次结构,强调视图定义的递归展开过程,确保视图定义不是递归的以避免无限循环。
  2. 触发器执行过程图:

    • 说明:图示展示了当执行INSERT、UPDATE或DELETE操作时,系统如何创建inserted和deleted临时表,并在触发器中使用这些表进行相应的数据操作。
    • 关键点:强调触发器在特定事件发生时自动执行,并利用临时表维护数据的前后状态。
  3. JDBC操作流程图:

    • 说明:图中展示了JDBC的典型操作流程,包括加载驱动、建立连接、创建语句、执行查询、处理结果集以及关闭连接的步骤。
    • 关键点:帮助理解JDBC在Java应用中与数据库交互的整体流程,提高编程实践的准确性。
  4. 联合查询与子查询示意图:

    • 说明:图示展示了如何通过JOIN操作和嵌套SELECT子查询在多个关系间进行数据整合。
    • 关键点:帮助理解复杂查询的结构和执行顺序,增强对多表联结与子查询逻辑的掌握。

第五章 高级SQL

5.1 重点

  1. SQL与编程语言的集成:

    • 理解如何通过不同的编程语言(如Java、Python、PHP等)访问和操作数据库。
  2. JDBC的使用:

    • 掌握Java应用程序中使用JDBC连接数据库、执行SQL语句以及处理结果集的流程。
  3. 函数与存储过程:

    • 理解数据库中的函数和存储过程的定义与调用,掌握其在业务逻辑封装中的应用。
  4. 触发器的设计与应用:

    • 学会设计DML触发器(AFTER和INSTEAD OF),并理解其在自动执行副作用操作中的作用。

5.2 难点

  1. 事务管理与连接池:

    • 理解事务的概念,掌握如何在应用程序中正确管理事务,避免数据不一致。
  2. 安全性问题:

    • 理解SQL注入攻击的原理及防范措施,如使用预编译语句(Prepared Statements)。
  3. 元数据的使用:

    • 熟悉如何通过JDBC获取数据库及结果集的元数据,以动态处理不同的数据库结构。

5.3 实例分析

例1:JDBC 连接与查询

任务: 使用JDBC连接到SQL Server数据库,执行查询并处理结果集。

示例代码:

import java.sql.*;

public class JDBCExample {
public static void main(String[] args) {
String dbURL = "jdbc:sqlserver://localhost:1433;databaseName=univdb";
String user = "userid";
String password = "passwd";

try (Connection conn = DriverManager.getConnection(dbURL, user, password);
Statement stmt = conn.createStatement()) {

String sql = "SELECT dept_name, AVG(salary) as avg_salary FROM instructor GROUP BY dept_name";
ResultSet rs = stmt.executeQuery(sql);

while (rs.next()) {
String deptName = rs.getString("dept_name");
float avgSalary = rs.getFloat("avg_salary");
System.out.println(deptName + " " + avgSalary);
}

rs.close();
} catch (SQLException e) {
System.out.println("SQLException: " + e.getMessage());
}
}
}

说明:


例2:使用预编译语句防止SQL注入

任务: 安全地向instructor表插入数据,防止SQL注入攻击。

示例代码:

import java.sql.*;

public class SecureInsertExample {
public static void main(String[] args) {
String dbURL = "jdbc:sqlserver://localhost:1433;databaseName=univdb";
String user = "userid";
String password = "passwd";

String insertSQL = "INSERT INTO instructor (ID, name, dept_name, salary) VALUES (?, ?, ?, ?)";

try (Connection conn = DriverManager.getConnection(dbURL, user, password);
PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {

// 设置参数
pstmt.setString(1, "88877");
pstmt.setString(2, "Perry");
pstmt.setString(3, "Finance");
pstmt.setInt(4, 125000);

// 执行插入
pstmt.executeUpdate();

// 插入另一条记录
pstmt.setString(1, "88878");
pstmt.setString(2, "John");
pstmt.setString(3, "Marketing");
pstmt.setInt(4, 110000);
pstmt.executeUpdate();

System.out.println("数据插入成功。");
} catch (SQLException e) {
System.out.println("SQLException: " + e.getMessage());
}
}
}

说明:


例3:触发器的创建与使用

任务: 为student表创建一个AFTER INSERT触发器,更新student_sum表中的学生总数。

示例SQL代码:

-- 创建student表
CREATE TABLE student (
stu_id INT IDENTITY(1,1) PRIMARY KEY,
stu_name VARCHAR(10),
stu_gender CHAR(2),
stu_age INT
);

-- 创建student_sum表
CREATE TABLE student_sum (
stuCount INT DEFAULT(0)
);

-- 创建AFTER INSERT触发器
CREATE TRIGGER trig_insert
ON student
AFTER INSERT
AS
BEGIN
DECLARE @stuNumber INT;
SELECT @stuNumber = COUNT(*) FROM student;

IF NOT EXISTS (SELECT * FROM student_sum)
INSERT INTO student_sum VALUES (0);

UPDATE student_sum SET stuCount = @stuNumber;
END;

-- 创建删除触发器
CREATE TRIGGER trig_delete
ON student
AFTER DELETE
AS
BEGIN
SELECT stu_id AS 已删除的学生编号, stu_name, stu_gender, stu_age
FROM deleted;
END;

-- 创建更新触发器
CREATE TRIGGER trig_update
ON student
AFTER UPDATE
AS
BEGIN
DECLARE @stuCount INT;
SELECT @stuCount = COUNT(*) FROM student;
UPDATE student_sum SET stuCount = @stuCount;

SELECT stu_id AS 更新前学生编号, stu_name AS 更新前学生姓名 FROM deleted;
SELECT stu_id AS 更新后学生编号, stu_name AS 更新后学生姓名 FROM inserted;
END;

说明:

注意事项:


4.4 第三章与第四章中提及的重要图示描述

  1. 视图的依赖关系图:

    • 说明:图中展示了视图之间的依赖关系,如视图physics_fall_2017_watson依赖于视图physics_fall_2017,而physics_fall_2017又依赖于基础关系course和section。
    • 关键点:展示了视图层次结构,强调视图定义的递归展开过程,确保视图定义不是递归的以避免无限循环。
  2. 触发器执行过程图:

    • 说明:图示展示了当执行INSERT、UPDATE或DELETE操作时,系统如何创建inserted和deleted临时表,并在触发器中使用这些表进行相应的数据操作。
    • 关键点:强调触发器在特定事件发生时自动执行,并利用临时表维护数据的前后状态。
  3. JDBC操作流程图:

    • 说明:图中展示了JDBC的典型操作流程,包括加载驱动、建立连接、创建语句、执行查询、处理结果集以及关闭连接的步骤。
    • 关键点:帮助理解JDBC在Java应用中与数据库交互的整体流程,提高编程实践的准确性。
  4. 联合查询与子查询示意图:

    • 说明:图示展示了如何通过JOIN操作和嵌套SELECT子查询在多个关系间进行数据整合。
    • 关键点:帮助理解复杂查询的结构和执行顺序,增强对多表联结与子查询逻辑的掌握。

第六章 基于E-R模型的数据库设计

6.1 重点

  1. 实体-关系模型(E-R模型):理解实体、属性、关系及其在数据库设计中的应用。
  2. 属性类型:掌握简单属性、复合属性、多值属性和派生属性的区别及其表示方法。
  3. 关系的基数约束:熟悉一对一、一对多、多对多等关系的特点及其在E-R图中的表示。
  4. 主键:了解实体集和关系集的主键选择及其在唯一标识实体中的作用。
  5. 弱实体集与标识关系:掌握弱实体集的定义及其与标识实体集之间的关系。

6.2 难点

  1. E-R图到关系模式的转换:如何准确地将E-R图中的各个元素映射到关系数据库的模式中。
  2. 处理多值属性:理解如何通过分离关系来消除多值属性带来的冗余。
  3. 聚合与泛化/特化:掌握在E-R模型中表示复杂关系和层次结构的方法。
  4. 设计规范化:了解如何通过规范化过程消除冗余和避免更新异常。
  5. 依赖约束的保持与无损分解:理解在关系模式分解过程中,如何保持功能依赖以及确保分解的无损性。

6.3 主要内容总结

1. 数据库设计过程概述

数据库设计过程通常包括以下几个阶段:

2. 实体-关系模型(E-R模型)

E-R模型用于概念化地描述数据库的结构,主要包括以下三个基本概念:

2.1 实体集与属性
2.2 属性类型
2.3 关系集与基数约束
2.4 弱实体集与标识关系

3. 从E-R图到关系模式的转换

将E-R图转换为关系模式时,需遵循以下步骤:

  1. 实体集转换:每个强实体集转换为一个关系,属性成为关系的字段,主键作为关系主键。
  2. 弱实体集转换:将弱实体集转换为关系,包含其标识实体集的主键作为外键,并结合自身的部分主键。
  3. 关系集转换:
    • 多对多关系:创建一个新的关系,包含两个参与实体集的主键作为外键。
    • 一对多或多对一关系:在“多”侧的关系中添加“一”侧的外键。
    • 一对一关系:可以选择任一侧添加对方的外键。
3.1 处理多值属性

多值属性通过创建新的关系来消除冗余。例如,“教师”的“电话号码”可以通过一个单独的关系 教师电话(教师ID,电话) 来表示。

3.2 复合属性的展开

复合属性被拆解为更小的简单属性。例如,“地址”可以拆分为“街道”、“城市”、“州”、“邮编”。

4. 扩展的E-R特性

4.1 泛化与特化(Generalization/Specialization)
4.2 聚合(Aggregation)

聚合用于表示关系集之间的关系,将一个或多个关系集视为一个整体来参与其他关系集。

5. 数据库设计中的常见问题与解决方案

5.1 冗余与更新异常
5.2 规范化

通过规范化过程(如达到第三范式、BCNF等),消除冗余,避免更新异常。

6. 替代符号表示

不同的E-R图符号如Chen符号、Crow’s Feet符号以及UML类图,有助于不同场景下的数据库设计和表示。

6.4 实例分析

实例1:大学信息维护

需求描述

一所大学需要维护以下信息:

设计过程
  1. 实体集定义

    • 系(Department):属性包括 dept_name(主键)、head、address。
    • 班级(Class):属性包括 class_id(主键)、name、grade。
    • 教师(Teacher):属性包括 teacher_id(主键)、name、age。
  2. 关系集定义

    • 隶属(Affiliation):一对多关系,连接 Department 和 Class。一个系可有多个班级,但班级只能属于一个系。
    • 聘请(Employment):一对多关系,连接 Department 和 Teacher。一个系聘请多个教师,一个教师被一个系聘请。
    • 班主任(Advisor):一对一关系,连接 Teacher 和 Class。一个教师最多担任一个班主任,一个班级必须有一个班主任。
  3. 关系模式转换

    • Department(dept_name [PK], head, address)
    • Class(class_id [PK], name, grade, dept_name [FK])
    • Teacher(teacher_id [PK], name, age, dept_name [FK])
    • Advisor(teacher_id [PK, FK], class_id [FK])
  4. SQL语句示例
    创建上述关系模式的SQL Server语句如下:

    CREATE TABLE Department (
    dept_name VARCHAR(50) PRIMARY KEY,
    head VARCHAR(50),
    address VARCHAR(100)
    );

    CREATE TABLE Teacher (
    teacher_id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    dept_name VARCHAR(50),
    FOREIGN KEY (dept_name) REFERENCES Department(dept_name)
    );

    CREATE TABLE Class (
    class_id INT PRIMARY KEY,
    name VARCHAR(50),
    grade INT,
    dept_name VARCHAR(50),
    FOREIGN KEY (dept_name) REFERENCES Department(dept_name)
    );

    CREATE TABLE Advisor (
    teacher_id INT PRIMARY KEY,
    class_id INT,
    FOREIGN KEY (teacher_id) REFERENCES Teacher(teacher_id),
    FOREIGN KEY (class_id) REFERENCES Class(class_id)
    );
重点难点解析

实例2:处理多值属性

需求描述

记录教师的多个电话号码和多个子女姓名。

设计过程

由于多值属性会导致数据冗余,需通过创建新的关系来处理。

  1. 实体集定义

    • 教师(Teacher):属性包括 teacher_id(主键)、name、age。
  2. 多值属性的处理

    • 教师电话(Teacher_Phone):teacher_id(外键)、phone_number。
    • 教师子女(Teacher_Child):teacher_id(外键)、child_name。
  3. 关系模式转换

    • Teacher(teacher_id [PK], name, age)
    • Teacher_Phone(teacher_id [FK], phone_number)
    • Teacher_Child(teacher_id [FK], child_name)
  4. SQL语句示例

    CREATE TABLE Teacher (
    teacher_id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT
    );

    CREATE TABLE Teacher_Phone (
    teacher_id INT,
    phone_number VARCHAR(20),
    FOREIGN KEY (teacher_id) REFERENCES Teacher(teacher_id)
    );

    CREATE TABLE Teacher_Child (
    teacher_id INT,
    child_name VARCHAR(50),
    FOREIGN KEY (teacher_id) REFERENCES Teacher(teacher_id)
    );
重点难点解析

6.5 图表与图示

1. E-R图的图形表示

实体集与属性
关系集与基数约束
弱实体集与标识关系

2. 从E-R图到关系模式的转换示意

原始E-R图示例:

[Department]---<Affiliation>---[Class]
[Department]---<Employment>---[Teacher]
[Teacher]---<Advisor>---[Class]

转换后的关系模式:

1. Department(dept_name [PK], head, address)
2. Class(class_id [PK], name, grade, dept_name [FK])
3. Teacher(teacher_id [PK], name, age, dept_name [FK])
4. Advisor(teacher_id [PK, FK], class_id [FK])

6.6 总结

数据库设计是一个系统性工程,需要从需求分析开始,经过概念设计、逻辑设计到物理设计的多个阶段。在E-R模型的帮助下,可以清晰地描述实体、属性和它们之间的关系。通过将E-R图转换为关系模式,并应用规范化理论,可以有效地消除冗余、避免更新异常,提升数据库的整体质量。

6.7 建议

  1. 深入理解E-R模型:通过实践绘制E-R图,掌握不同关系的表示方法。
  2. 多练习E-R图到关系模式的转换:理解各种关系的映射规则,特别是处理多值属性和弱实体集时的细节。
  3. 掌握规范化理论:学习不同范式的定义及其目的,应用规范化过程优化数据库设计。
  4. 使用专业工具辅助设计:利用数据库设计工具(如ERwin、PowerDesigner)绘制E-R图,提高设计效率和准确性。

第七章 标准化

7.1 关系数据库设计中的问题

重点难点:

内容摘要:


7.2 不良数据依赖与关系分解

重点难点:

内容摘要:

实例分析:


7.3 正规化与范式

重点难点:

内容摘要:

实例分析:


7.4 规范化算法与依赖闭包

重点难点:

内容摘要:

实例分析:


7.5 依赖与范式的深入理解

重点难点:

内容摘要:

实例分析:


7.6 无损连接与依赖保留的算法

重点难点:

内容摘要:

实例分析:


7.7 多值依赖与第四范式(4NF)

重点难点:

内容摘要:

实例分析:


7.8 规范化注意事项与高级范式

重点难点:

内容摘要:

实例分析:


7.9 综合实例与规范化总结

重点难点:

内容摘要:

实例分析:


7.10 多值依赖与进一步规范化(7.117 - 7.140)

重点难点:

内容摘要:

实例分析:


7.11 方法论与最佳实践

重点难点:

内容摘要:

实例分析:


7.12 总结与复习要点

学习建议:


7.13 附录:SQL Server环境下的示例SQL语句

示例1:创建和分解worker关系

-- 创建原始worker表
CREATE TABLE worker (
name VARCHAR(50),
branch CHAR(1),
manager VARCHAR(50)
);

-- 插入示例数据
INSERT INTO worker VALUES
('Jones', 'A', 'Frank'),
('Smith', 'B', 'David'),
('Curry', 'C', 'Frank'),
('Larry', 'B', 'David'),
('Julia', 'C', 'Frank');

-- 分解为worker1和branch表
CREATE TABLE worker1 (
name VARCHAR(50),
branch CHAR(1)
);

CREATE TABLE branch (
branch CHAR(1) PRIMARY KEY,
manager VARCHAR(50)
);

-- 插入分解后的数据
INSERT INTO worker1 (name, branch)
SELECT name, branch FROM worker;

INSERT INTO branch (branch, manager)
SELECT DISTINCT branch, manager FROM worker;

示例2:创建触发器以维护student_sum表

-- 创建学生表
CREATE TABLE student (
stu_id INT IDENTITY(1,1) PRIMARY KEY,
stu_name VARCHAR(10),
stu_gender CHAR(2),
stu_age INT
);

-- 创建存储学生人数的student_sum表
CREATE TABLE student_sum (
stuCount INT DEFAULT(0)
);

-- 创建插入触发器
CREATE TRIGGER trig_insert
ON student
AFTER INSERT
AS
BEGIN
DECLARE @stuNumber INT;
SELECT @stuNumber = COUNT(*) FROM student;
IF NOT EXISTS (SELECT * FROM student_sum)
INSERT INTO student_sum VALUES(0);
UPDATE student_sum SET stuCount = @stuNumber;
END;

-- 创建删除触发器
CREATE TRIGGER trig_delete
ON student
AFTER DELETE
AS
BEGIN
SELECT stu_id AS 已删除的学生编号, stu_name, stu_gender, stu_age
FROM deleted;
END;

-- 创建更新触发器
CREATE TRIGGER trig_update
ON student
AFTER UPDATE
AS
BEGIN
DECLARE @stuCount INT;
SELECT @stuCount = COUNT(*) FROM student;
UPDATE student_sum SET stuCount = @stuCount;

SELECT stu_id AS 更新前学生编号, stu_name AS 更新前学生姓名 FROM deleted;
SELECT stu_id AS 更新后学生编号, stu_name AS 更新后学生姓名 FROM inserted;
END;

注意事项:


通过以上总结,学生可以系统地掌握数据库规范化的理论与实践,理解不同范式的意义及其应用,熟悉在SQL Server环境下实现规范化设计的具体步骤与SQL语句。


第八章 多值依赖(MVDS)

8.1 重点

  1. 多值依赖(MVD)的定义与理解:

    • 理解多值依赖的概念,即在关系模式中,如果属性集合X多值依赖于Y,意味着对于每个X的值,Y的值是相互独立的。
  2. 第四范式(4NF)的定义及其与BCNF的关系:

    • 熟悉4NF的定义:如果一个关系模式中的每一个非平凡的多值依赖都由超级键决定,则该关系模式属于4NF。
    • 理解4NF比BCNF更严格,4NF不仅处理函数依赖,还处理多值依赖。
  3. 多值依赖的规约与分解:

    • 学会根据MVD进行关系模式的分解,以消除数据冗余。
    • 了解4NF分解的方法,确保分解后的关系模式依然保持无损连接。

8.2 难点

  1. 多值依赖的识别与应用:

    • 在复杂的关系模式中识别哪些属性之间存在多值依赖,区分多值依赖和函数依赖。
  2. 4NF与BCNF的区别与联系:

    • 理解虽然每一个函数依赖都是多值依赖,但反之则不然,导致4NF包含更多的约束条件。
  3. 复杂分解的正确性验证:

    • 确保通过多值依赖分解后的关系模式依然满足原有的数据依赖和保持无损连接。

8.3 实例分析

实例1:多值依赖的定义与应用

关系模式:Drinkers(name, addr, phones, beersLiked)

多值依赖:

解释:

SQL Server 中的示例操作:

假设有以下数据:

INSERT INTO Drinkers (name, addr, phones, beersLiked) VALUES 
('sue', 'a', 'p1', 'b1'),
('sue', 'a', 'p2', 'b2'),
('sue', 'a', 'p2', 'b1'),
('sue', 'a', 'p1', 'b2');

分解为4NF:

  1. 分解出电话信息:

    CREATE TABLE Drinkers_Phones (
    name VARCHAR(50),
    addr VARCHAR(100),
    phones VARCHAR(20),
    PRIMARY KEY (name, phones)
    );

    INSERT INTO Drinkers_Phones (name, addr, phones)
    SELECT DISTINCT name, addr, phones FROM Drinkers;
  2. 分解出喜欢的啤酒信息:

    CREATE TABLE Drinkers_BeersLiked (
    name VARCHAR(50),
    beersLiked VARCHAR(50),
    PRIMARY KEY (name, beersLiked)
    );

    INSERT INTO Drinkers_BeersLiked (name, beersLiked)
    SELECT DISTINCT name, beersLiked FROM Drinkers;

这样可以消除原表中的多值依赖,避免数据冗余。

实例2:4NF 的分解

原关系模式:Drinkers(name, addr, phones, beersLiked, manf)

多值依赖:

分解步骤:

  1. 创建 Drinkers_AreaPhone 表:

    CREATE TABLE Drinkers_AreaPhone (
    name VARCHAR(50),
    areaCode VARCHAR(10),
    phone VARCHAR(20),
    PRIMARY KEY (name, areaCode, phone)
    );
  2. 创建 Drinkers_BeersManf 表:

    CREATE TABLE Drinkers_BeersManf (
    name VARCHAR(50),
    beersLiked VARCHAR(50),
    manf VARCHAR(50),
    PRIMARY KEY (name, beersLiked, manf)
    );
  3. 移除原表中的多值依赖:

    CREATE TABLE Drinkers_Info (
    name VARCHAR(50),
    addr VARCHAR(100),
    PRIMARY KEY (name)
    );

确保分解无损连接:

通过连接 Drinkers_AreaPhone 和 Drinkers_BeersManf 可以恢复原始数据。


8.4 图表与图示

1. 多值依赖的图示描述

2. 4NF 分解的流程图



第九章 应用程序开发

9.1 重点

  1. 应用程序架构与Web基础:

    • 理解现代应用程序的多层架构(前端、中间层、后端)及其各自的职责。
    • 掌握Web技术的基础,如HTML、JavaScript、Servlets、JSP等。
  2. 快速应用程序开发(RAD)工具与框架:

    • 熟悉常用的RAD工具和Web框架,如Java Server Faces (JSF)、Ruby on Rails、ASP.NET等,提高开发效率。
  3. 应用性能优化:

    • 了解缓存技术(如连接池、查询缓存、HTML缓存)以提高Web应用的响应速度和处理能力。
  4. 应用安全性:

    • 深入理解Web应用常见的安全问题,如SQL注入、跨站脚本攻击(XSS)、跨站请求伪造(CSRF)等,并掌握相应的防护措施。
    • 掌握加密技术及其在数据库中的应用,确保数据传输和存储的安全。

9.2 难点

  1. 会话管理与状态保持:

    • 理解无状态的HTTP协议如何通过会话(Session)管理用户状态,掌握Cookies、Session等技术。
  2. 触发器的复杂应用:

    • 设计复杂的触发器以维护数据一致性或实现自动化操作,同时避免触发器带来的性能问题和意外副作用。
  3. 跨域认证与单点登录(SSO):

    • 理解如何实现跨域的安全认证机制,如SAML、OpenID,以及在分布式系统中的认证流程。
  4. 加密技术的结合与应用:

    • 了解对称加密与非对称加密的结合使用(混合加密),以及在实际数据库中的具体应用场景和配置方法。

9.3 实例分析

例1:创建和使用触发器

任务: 为student表创建一个AFTER UPDATE触发器,更新student_sum表中的学生总数,并记录更新前后的学生信息。

示例SQL代码:

-- 创建student表
CREATE TABLE student (
stu_id INT IDENTITY(1,1) PRIMARY KEY,
stu_name VARCHAR(10),
stu_gender CHAR(2),
stu_age INT
);

-- 创建student_sum表
CREATE TABLE student_sum (
stuCount INT DEFAULT(0)
);

-- 创建AFTER UPDATE触发器
CREATE TRIGGER trig_update
ON student
AFTER UPDATE
AS
BEGIN
DECLARE @stuCount INT;
SELECT @stuCount = COUNT(*) FROM student;
UPDATE student_sum SET stuCount = @stuCount;

-- 显示更新前后的学生信息
SELECT stu_id AS 更新前学生编号, stu_name AS 更新前学生姓名 FROM deleted;
SELECT stu_id AS 更新后学生编号, stu_name AS 更新后学生姓名 FROM inserted;
END;

说明:


例2:防范SQL注入攻击

任务: 通过编写Servlet,确保用户输入通过预编译语句处理,防止SQL注入。

示例Java Servlet代码片段:

// 假设已经建立数据库连接conn
String persontype = request.getParameter("persontype");
String name = request.getParameter("name");

if(persontype.equals("student")) {
String sql = "SELECT ID, name, dept_name FROM student WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
ResultSet rs = pstmt.executeQuery();

out.println("<table BORDER COLS=3>");
out.println("<tr> <td>ID</td> <td>Name</td> <td>Department</td> </tr>");
while(rs.next()) {
String ID = rs.getString("ID");
String studentName = rs.getString("name");
String deptName = rs.getString("dept_name");
out.println("<tr> <td>" + ID + "</td><td>" + studentName + "</td><td>" + deptName + "</td></tr>");
}
out.println("</table>");
rs.close();
pstmt.close();
} else {
// 类似处理instructor类型
}

说明:


例3:使用JSP实现动态网页

任务: 编写一个JSP页面,根据用户输入的名字显示问候语。

示例JSP代码:

<html>
<head>
<title>Hello</title>
</head>
<body>
<%
String name = request.getParameter("name");
if (name == null) {
%>
Hello World
<%
} else {
%>
Hello, <%= name %>
<%
}
%>
</body>
</html>

说明:

9.4 图表与图示

1. Web 应用程序三层架构图示描述

2. Servlet 处理流程图示描述

3. 防范XSS攻击的流程图示描述


第十章 物理存储系统和文件组织

10.1 物理存储介质分类

物理存储介质按数据存储的特性分为以下两类:

影响存储介质选择的因素:


10.2 存储层次结构

存储设备按照速度、成本和存储容量形成一个层次结构:

实例分析:


10.3 磁盘存储机制

磁盘存储的关键技术细节:

实例分析:
假设一块硬盘的平均定位时间为5ms,旋转延迟为5ms,传输速率为100MB/s,一次读取1MB数据:


10.4 磁盘可靠性与性能


10.5 闪存存储


10.6 RAID(独立磁盘冗余阵列)

RAID通过并行和冗余提升存储系统的性能和可靠性:

实例分析:
RAID 1的两个磁盘MTTF均为10万小时,修复时间为10小时,则系统数据丢失的平均时间为:


10.7 文件组织

数据库存储为文件集合,每个文件由记录组成:


10.8 文件组织策略


10.9 列存储与压缩


10.10 缓存管理

实例分析:
嵌套循环连接可能导致LRU策略性能低下,因为循环内的访问模式会不断驱逐之前加载的块。


10.11 数据字典(系统目录)

数据字典存储数据库的元数据,例如:

元数据的高效访问对数据库系统的运行至关重要。



第十一章 索引

11.1 索引的基本概念

关键概念

评价指标


11.2 有序索引

有序索引是基于搜索键值排序的索引方式。

分类

  1. 聚簇索引(Clustering Index):

    • 索引的键值与文件的物理顺序一致。
    • 通常是主键,但不一定必须是主键。
    • 又称主索引(Primary Index)。
  2. 非聚簇索引(Non-Clustering Index):

    • 索引的键值与文件的物理顺序无关。
    • 又称次级索引(Secondary Index)。
  3. 密集索引(Dense Index):

    • 每个搜索键值都有一个索引记录。
    • 例子:对instructor关系的ID属性建立密集索引。
      • 解析:每个ID值在索引中都有一个对应的条目,指向实际数据记录。
  4. 稀疏索引(Sparse Index):

    • 仅为部分搜索键值建立索引条目。
    • 适用场景:文件按搜索键排序,例如每个块的第一个键值。
    • 优缺点:稀疏索引节省空间,但查找速度较慢。

例子解析:
假设文件根据dept_name排序,对其建立稀疏索引。要查找Physics,首先找到小于Physics的最大键值(例如Mathematics),然后从指针指定的位置顺序扫描。


11.3 多级索引

当索引文件太大无法全部加载到内存时:

优点:通过分层结构减少磁盘访问次数,提高查找效率。
缺点:插入或删除时需要更新所有层次的索引。


11.4 B+树索引

B+树是一种动态多级索引结构,广泛用于数据库系统中。

特点

优点

例子解析:

  1. 插入“Adams”:

    • 找到适当的叶节点,如果节点已满,拆分节点,将部分键值移至新节点,并将新节点的信息插入父节点。
    • 如果父节点也满,继续向上拆分,可能导致树高度增加。
  2. 删除“Singh”:

    • 如果删除导致节点不足,将其与相邻节点合并,或从邻居节点借用键值。
    • 如果父节点变得不足,继续调整,可能导致树高度减少。

11.5 哈希索引

哈希索引通过哈希函数将搜索键值映射到桶地址。

分类

  1. 静态哈希:

    • 桶数量固定。
    • 缺点:当文件大小动态变化时,可能导致桶溢出或空间浪费。
  2. 动态哈希:

    • 线性哈希(Linear Hashing):增量扩展桶,以适应文件增长。
    • 可扩展哈希(Extendable Hashing):通过增加哈希表的深度而不增加桶数量。

例子解析:

对instructor的ID属性建立哈希索引:

  1. 将ID值通过哈希函数映射到桶。
  2. 如果多个键值映射到同一桶,使用溢出链表存储额外记录。

优缺点对比:


11.6 复合索引

复合搜索键:包含多个属性的搜索键,例如(dept_name, salary)。

特点

例子解析:
假设有索引(dept_name, salary):

  1. 查询条件dept_name="Finance" AND salary=80000:

    • 索引直接定位到满足条件的记录。
  2. 查询条件dept_name="Finance" AND salary<80000:

    • 索引可快速定位到Finance,然后顺序扫描满足salary<80000的记录。
  3. 查询条件dept_name<"Finance" AND salary=80000:

    • 无法高效利用索引,因为dept_name的范围查询优先级更高。

11.7 位图索引

哈希索引通过哈希函数将搜索键值映射到桶地址。

特点

查询操作

优点:空间开销小,查询效率高。
缺点:不适合动态变化的数据。


11.8 索引的创建与管理

SQL中的索引定义

数据库自动创建索引


11.9 索引的优化选择

索引的使用需权衡查询性能与更新成本:



第十三章 查询优化

13.1 查询优化的目标

查询优化的目标是通过生成低成本的执行计划来提高查询性能。


13.2 关系代数等价性与规则

等价性定义

两个关系代数表达式在所有合法的数据库实例上生成相同的结果集,则它们是等价的。

常用等价规则

  1. 选择操作规则:

    • 选择的分解与交换:

      • σθ1∧θ2(E) ≡ σθ1(σθ2(E))
      • σθ1(σθ2(E)) ≡ σθ2(σθ1(E))
    • 选择与笛卡尔积、连接的结合:

      • σθ(E1 × E2) ≡ E1 ⨝θ E2
      • σθ1(E1 ⨝θ2 E2) ≡ E1 ⨝θ1∧θ2 E2
  2. 投影操作规则:

    • 只保留最后一个投影操作:
      πL1(πL2(...πLn(E)...)) ≡ πL1(E),其中 L1 ⊆ L2 ⊆ ... ⊆ Ln。
  3. 连接操作规则:

    • 连接的交换性:E1 ⨝ E2 ≡ E2 ⨝ E1
    • 连接的结合性:(E1 ⨝ E2) ⨝ E3 ≡ E1 ⨝ (E2 ⨝ E3)
  4. 选择与连接的分配性:

    • σθ(E1 ⨝ E2) ≡ (σθ(E1)) ⨝ E2,条件是 θ 只涉及 E1 的属性。
  5. 投影与连接的分配性:

    • πL1∪L2(E1 ⨝ E2) ≡ (πL1(E1)) ⨝ (πL2(E2)),条件是 L1 和 L2 包含连接所需的所有属性。
  6. 集合操作规则:

    • 并集和交集的交换性与结合性:
      • E1 ∪ E2 ≡ E2 ∪ E1,(E1 ∪ E2) ∪ E3 ≡ E1 ∪ (E2 ∪ E3)
    • 选择与集合操作的分配性:
      • σθ(E1 ∪ E2) ≡ σθ(E1) ∪ σθ(E2)

例子解析:
查询:查找Music系的教师姓名及其在2017年授课的课程标题。

SELECT ARTIST.NAME, COURSE.TITLE
FROM ARTIST, TEACHES, COURSE
WHERE ARTIST.dept_name = 'Music'
AND TEACHES.year = 2017
AND ARTIST.ID = TEACHES.ID
AND TEACHES.course_id = COURSE.course_id;
  1. 初始表达式:
    πname, title(σdept_name='Music'∧year=2017(instructor ⨝ teaches ⨝ course))

  2. 使用连接结合性:
    (instructor ⨝ teaches) ⨝ course 转换为 instructor ⨝ (teaches ⨝ course)

  3. 应用选择下推规则:
    σdept_name='Music'(instructor) ⨝ σyear=2017(teaches) ⨝ course

  4. 提前投影:减少中间结果的大小。


13.3 基于成本的优化

步骤

  1. 生成等价表达式:

    • 使用关系代数等价规则生成多个等价表达式。
  2. 注释表达式:

    • 为每个表达式选择具体的算法(如嵌套循环连接、哈希连接等)。
  3. 估算成本:

    • 基于统计信息估算每种执行计划的成本。
  4. 选择最低成本计划:

    • 比较所有计划,选择成本最低的一个。

成本估算

例子解析:
学生表(student)和选课表(takes):


13.4 动态规划与连接顺序

连接顺序优化

算法

  1. 对于每个子集 S:

    • 尝试所有可能的分割 S1 和 S2(S1 ⨝ S2)。
    • 计算每种分割的成本,选择最低成本的方案。
  2. 存储每个子集的最优计划,避免重复计算。

复杂性:


13.5 启发式优化

启发式规则

  1. 选择操作尽早执行:

    • 减少待处理的元组数量。
  2. 投影操作尽早执行:

    • 减少待处理的属性数量。
  3. 优先执行选择性强的操作:

    • 选择性强的操作通常会显著减少中间结果的大小。
  4. 避免笛卡尔积:

    • 替换为连接操作。

例子解析:
查询:查找专辑名称为“Andy’s OG Remix”的艺术家姓名。

SELECT ARTIST.NAME
FROM ARTIST, APPEARS, ALBUM
WHERE ARTIST.ID = APPEARS.ARTIST_ID
AND APPEARS.ALBUM_ID = ALBUM.ID
AND ALBUM.NAME = "Andy’s OG Remix";
  1. 分解谓词:

    • ARTIST.ID = APPEARS.ARTIST_ID
    • APPEARS.ALBUM_ID = ALBUM.ID
    • ALBUM.NAME = "Andy’s OG Remix"
  2. 谓词下推:

    • 将 ALBUM.NAME = "Andy’s OG Remix" 下推到 ALBUM 中,减少中间结果。
  3. 替换笛卡尔积:

    • 用连接代替笛卡尔积:ARTIST ⨝ APPEARS ⨝ ALBUM。
  4. 提前投影:

    • 删除不必要的属性,减少中间结果的大小。

13.6 统计信息与直方图

例子解析:
假设V(age, people) = 5, nr = 5:

  1. 查询 age=2:

    • 满足条件的记录数为 nr / V(age, people) = 5 / 5 = 1。
  2. 查询 age>=2:

    • 满足条件的记录数为 nr * (Amax - 2 + 1) / (Amax - Amin + 1)。
    • 结果为 5 * (4 - 2 + 1) / (4 - 0 + 1) = 3。


第十四章 事务

14.1 事务的基本概念

事务(Transaction)

例子:
将$50从账户A转移到账户B的事务:

  1. 读取账户A余额。
  2. 从账户A扣除50。
  3. 写回账户A的新余额。
  4. 读取账户B余额。
  5. 向账户B添加50。
  6. 写回账户B的新余额。

问题:


14.2 事务的ACID属性

事务需要满足以下四个关键特性以保证数据库的一致性:

  1. 原子性(Atomicity):

    • 事务的所有操作要么全部执行,要么全部不执行。
    • 解决方法:通过日志记录,在事务失败时回滚未完成的操作。
  2. 一致性(Consistency):

    • 事务的执行必须保证数据库从一个一致状态转变为另一个一致状态。
    • 例子:在转账事务中,A和B账户余额之和在事务执行后保持不变。
  3. 隔离性(Isolation):

    • 事务彼此独立运行,每个事务的中间状态对其他事务不可见。
    • 问题:如果隔离性未得到保证,可能导致数据不一致。
  4. 持久性(Durability):

    • 一旦事务提交,其对数据库的修改必须永久保存,即使系统崩溃。
    • 解决方法:使用日志或稳定存储。

14.3 事务的状态

事务的生命周期由以下几种状态组成:

例子:

BEGIN TRANSACTION;
UPDATE account SET money = money - 50 WHERE no = 'A';
UPDATE account SET money = money + 50 WHERE no = 'B';
COMMIT;

14.4 调度与并发执行

调度(Schedule)

并发执行的优点

  1. 提高处理器和磁盘的利用率。
  2. 减少事务的平均响应时间。

并发调度的问题

如果未正确处理并发调度,可能导致以下数据不一致性:

  1. 丢失修改:

    • 两个事务同时修改同一数据,一个事务的修改被另一个事务覆盖。

    • 例子:

      T1: read(A); A := A + 100; write(A);
      T2: read(A); A := A + 200; write(A);

      如果T1和T2同时执行,T2的写入会覆盖T1的结果,导致T1的修改丢失。

  2. 不可重复读:

    • 一个事务在两次读取同一数据时,数据值发生了变化。

    • 例子:

      T1: read(A); print(A); read(A); print(A);
      T2: read(A); A := A + 100; write(A);

      T1读取两次A的值可能不同。

  3. 读“脏”数据:

    • 一个事务T2读取了另一个事务T1未提交的数据,如果T1回滚,则T2读取的数据无效。

    • 例子:

      T1: read(A); A := A + 100; write(A); rollback;
      T2: read(A); print(A);

      T2读取了T1未提交的修改,导致读“脏”数据。


14.5 可串行化(Serializability)

定义

一个调度是可串行化的,如果它的执行效果与某个串行调度相同。

冲突可串行化(Conflict Serializability)

检测冲突可串行化

通过优先图(Precedence Graph)检测:

  1. 图中每个节点表示一个事务。
  2. 如果事务Ti的操作在事务Tj之前访问了数据,则添加一条从Ti到Tj的边。
  3. 判断方法:如果优先图无环,则调度是冲突可串行化的。

例子:


14.6 恢复性调度

恢复性(Recoverable)

如果事务Tj依赖于事务Ti写入的数据,则Tj必须在Ti提交后才能提交。

级联回滚(Cascading Rollback)


14.7 事务隔离级别

SQL-92定义了四种隔离级别,允许不同程度的并发性:

  1. Serializable(可串行化):

    • 最严格的隔离级别,保证事务调度可串行化。
  2. Repeatable Read(可重复读):

    • 防止不可重复读,但无法避免幻影读。
  3. Read Committed(读已提交):

    • 只能读取已提交的数据,可能导致不可重复读。
  4. Read Uncommitted(读未提交):

    • 允许读取未提交的数据,可能导致读“脏”数据。

例子解析:
事务1:

SELECT * FROM accounts WHERE balance > 1000;

事务2:

UPDATE accounts SET balance = balance - 100 WHERE id = 1;


第十五章 并发控制

15.1 引言:并发控制的必要性


15.2 基于锁的并发控制

2.1 锁的基本概念

例子:
事务T2读取两个数据项:

T2: lock-S(A); read(A); unlock(A);
lock-S(B); read(B); unlock(B);
display(A + B)

这种方式不能保证可串行化,因为没有控制事务间的交互。


2.2 两阶段锁协议(2PL)

例子:

  1. 符合两阶段锁协议:

    T1: lock-S(B); lock-X(A); read(B); read(A);
    unlock(B); write(A); unlock(A)
  2. 不符合两阶段锁协议:

    T1: lock-S(B); read(B); unlock(B);
    lock-X(A); read(A); write(A); unlock(A)

扩展:

  1. 严格两阶段锁协议:事务直到提交或回滚后才释放其所有的排它锁,避免了级联回滚。
  2. 严格强制两阶段锁协议:事务直到提交或回滚后才释放所有的锁,保证了事务的可恢复性和串行化顺序。

2.3 死锁处理


15.3 基于时间戳的并发控制

3.1 时间戳协议

读操作规则:

  1. 如果TS(Ti) < W-timestamp(Q),表示Ti试图读取已被覆盖的数据,Ti回滚。
  2. 如果TS(Ti) >= W-timestamp(Q),执行读取操作,并更新R-timestamp(Q)。

写操作规则:

  1. 如果TS(Ti) < R-timestamp(Q),表示Ti生成的值已经被其他事务读取,Ti回滚。
  2. 如果TS(Ti) < W-timestamp(Q),表示Ti试图写入过期数据,Ti回滚。
  3. 否则,执行写操作,并更新W-timestamp(Q)。

优点:

3.2 Thomas写规则


15.4 多版本并发控制(MVCC)

4.1 多版本协议的核心思想

4.2 多版本时间戳排序(MVTO)

优点:

4.3 快照隔离(Snapshot Isolation, SI)


15.5 其他并发控制协议

5.1 图锁协议

5.2 多粒度锁


15.6 总结

本章介绍了数据库并发控制的核心机制和协议,包括基于锁的控制、时间戳协议和多版本并发控制。

重点内容:

  1. 两阶段锁协议及其扩展(严格2PL、强制2PL)。
  2. 时间戳协议和Thomas写规则。
  3. 多版本控制中的快照隔离(SI)。
  4. 死锁的检测与预防。

实例分析:通过多个调度示例,详细阐述了并发控制协议在实际场景中的应用及其优缺点。

关键思想:在保证事务ACID特性的基础上,最大化并发性和系统性能。



第十六章 恢复系统

16.1 恢复系统的目标


16.2 故障分类

  1. 事务故障:

    • 逻辑错误:由于内部错误(如违反约束条件)导致事务无法完成。
    • 系统错误:如死锁,数据库系统需要终止某个事务。
  2. 系统崩溃:

    • 由于硬件或软件故障导致系统崩溃。
    • 假设:非易失性存储的数据不会被系统崩溃破坏(Fail-Stop假设)。
  3. 磁盘故障:

    • 硬盘损坏或部分数据丢失。
    • 使用校验和检测磁盘故障。

16.3 存储结构

  1. 易失性存储:

    • 不会在系统崩溃后保留数据。
    • 例如:主存、缓存。
  2. 非易失性存储:

    • 系统崩溃后保留数据。
    • 例如:磁盘、闪存。
  3. 稳定存储:

    • 理想化的存储,能够抵抗所有故障。
    • 实现方式:保持多个副本(如RAID系统)。

16.4 日志和恢复机制

4.1 日志记录

例子:
事务T0将$50从账户A转移到账户B:

  1. 日志记录:

    <T0 start>
    <T0, A, 1000, 950>
    <T0, B, 2000, 2050>
    <T0 commit>
  2. 如果系统崩溃:

    • 若<T0 commit>存在,则需要重做(Redo)。
    • 若<T0 commit>不存在,则需要回滚(Undo)。

4.2 Undo和Redo操作

  1. Undo(回滚):

    • 将事务所做的修改恢复为旧值。
    • 写入补偿日志记录:<Ti, X, V1>。
  2. Redo(重做):

    • 将事务提交的修改重新应用于数据库。

恢复规则:

4.3 恢复算法

  1. Redo阶段:

    • 从最近的检查点开始,向前扫描日志,对所有提交的事务重做操作。
  2. Undo阶段:

    • 从日志末尾向后扫描,对未完成的事务进行回滚操作。

示例:
假设以下日志记录:

<checkpoint L={T0, T1}>
<T0 start>
<T0, A, 1000, 950>
<T0, B, 2000, 2050>
<T1 start>
<T1, C, 700, 600>
<T0 commit>

16.5 检查点(Checkpoint)

5.1 检查点的作用

5.2 恢复时的优化

例子:
日志:

<checkpoint L={T1, T2}>
<T1 start>
<T1, A, 100, 200>
<T1 commit>
<T2 start>
<T2, B, 300, 400>

16.6 Write-Ahead Logging (WAL)


16.7 数据库缓冲管理

步骤:

  1. 写入日志记录。
  2. 刷新日志到稳定存储。
  3. 将数据块写回磁盘。

16.8 模糊检查点(Fuzzy Checkpointing)


16.9 远程备份系统

9.1 目的

9.2 工作原理

  1. 主站点将日志记录传输到备份站点。
  2. 备份站点应用日志更新本地数据库。
  3. 主站点故障后,备份站点接管事务处理。

9.3 提交策略


16.10 总结

本章详细讲解了数据库恢复的核心机制,包括日志记录、Undo/Redo操作、检查点和远程备份系统。通过具体的例子说明了如何在事务失败或系统崩溃后恢复数据库的一致性。重难点如下:

通过这些机制,数据库系统能够在故障后快速恢复,同时保证事务的ACID特性。


# SQL Server# Database