《数据库系统》概念整理
注意:下文中的“数据库”大部分时候都是“数据库系统”的简称,而非严格定义下的数据库。
第1章:引言
数据:描述事物的符号记录,数据库的基本对象。
数据库:长期储存在计算机内、有组织的、可共享的大量数据集合。
数据库管理系统:由一个互相关联的数据的集合和一组用以访问这些数据的程序组成,是位于用户与操作系统之间的一层数据管理软件。
数据库系统:在计算机系统中引入数据库后的系统,由数据库、数据库管理系统、应用系统(及其开发工具) 、数据库管理员(和用户)构成。
文件处理系统的弊端:数据的冗余和不一致、数据访问困难、数据孤立、完整性问题、原子性问题、并发访问异常、安全性问题
数据模型:数据库结构的基础。有关系模型、实体-联系模型、基于对象的数据模型、半结构化数据模型四种,网状模型和层次模型已经被淘汰。
存储管理器的数据结构:数据文件(存数据库)、数据字典(存元数据,是数据库的数据库)、索引
数据文件可以借助操作系统以文件形式存在本地,也可以不借助操作系统直接存入磁盘。
数据抽象的三个层次:物理层(描述数据真实的存储方式和底层复杂的数据结构)、逻辑层(描述数据间的关系和数据库中存储什么数据)、视图层(只描述整个数据库的某一部分)
根据描述数据库设计的层次,数据库系统可分为不同模式:物理模式、逻辑模式、子模式
物理数据独立性:应用程序不依赖物理模式,物理模式隐藏在逻辑模式下,可以在应用程序不受影响的情况下轻易更改。即使物理模式改变了,也无需重写应用程序。
数据库的层次结构:客户/服务器式数据库可以分为两层或三层。两层-前端/后端,前端直接和后端的数据库通信,客户机上的应用程序通过SQL来调用数据库(JDBC/ODBC);三层-前端/应用服务器/数据库服务器,应用程序的业务逻辑嵌入到应用服务器内,客户端通过表单与应用服务器通信,应用服务器与数据库通信以访问数据。
数据库DBA:模式定义、存储结构及存取方式定义、模式及物理组织修改、数据访问授权、日常维护(定期备份等)
数据操纵语言DML(增删改查)、数据定义语言DDL(create)、数据控制语句DCL(grant, revoke等)
第2章:关系模型
关系模型中,唯一的数据结构是关系,即二维表。
元组:行;属性:列;属性的域:某一属性的取值集合。所有属性的域都必须是原子的。
数据库模式:数据库的逻辑设计;数据库实例:某一时刻数据库中的数据的快照。关系模式、关系实例与之类似。
超码:一个或多个属性的集合,这个集合可以唯一地区分出一个元组(一行)
候选码:最小超码,可能有多个;主码:人为选中,作为一行的区分标准的候选码。
外码:关系\(r_1\)的属性中可能包含了关系\(r_2\)的主码,这个属性在\(r_1\)上称作参照\(r_2\)的外码,\(r_1\)称作外码依赖的参照关系,\(r_2\)称作外码依赖的被参照关系
参照完整性约束:参照关系中的任意元组的特定属性的取值必须等于被参照关系中某个元组的特定属性的取值。
查询语言:用户用来从数据库中请求获取信息的语言
过程化语言:用户指导系统对数据库执行一系列操作来计算结果
非过程化语言:用户只描述所需信息,不给出获取该信息的具体过程
第345章:SQL
SQL:结构化查询语言
select, from, where, group by, having, order by, as, like, escape, not like, with, delete from, insert into...values, update...set, case, create table ... as (select...)/like ..., create view ... as
distinct, all, natural join, desc, asc, union, intersect, except, is null, is not null, avg, min, max, sum, count, in, not in, some, all, exists, not exists, unique, join...on/using, (natural) left outer join, right outer join, full outer join
完整性约束:primary key、foreign key约束(参照完整性约束)、not null约束、unique约束、可延迟约束...
完整性约束保证用户对数据库的修改不会破坏数据一致性,SQL禁止破坏完整性约束的数据库更新。
断言:一个谓词,表达了我们希望数据库总能满足的一个条件。域约束和参照完整性约束是断言的特殊形式。
\(create\ assertion\ <assertion-name>\ check\ <predicate>\)
如果断言经系统检测有效,今后只有不破坏断言的数据库修改(和断言)能被允许。
视图:虚关系,长期保存查询结果,使用时重新计算。视图保证安全性,且更符合特定用户直觉的个人化的关系集合。数据库的一个主要目的是为用户提供数据的抽象视图,隐藏数据存储和维护的细节。
一般不允许对视图关系进行修改,视图可更新的条件是:
- from字句中只有一个数据库关系
- select子句中只包含关系的属性名,不包含表达式、聚集或distinct
- 没有出现在select中的属性可以取null(无not null约束,也不构成主码)
- 查询中不含有group by或having
物化视图:会被存储的视图关系。如果用于定义视图的实际关系改变,视图也跟着修改。
索引:\(create\ index\ INDEX\_NAME\ on\ TABLE(COLUMN)\) 以COLUMN为搜索码建立索引
\(create\ unique\ index\ INDEX\_NAME\ on\ TABLE(COLUMN)\) 声明该搜索码是候选码(若不是会报错)
删除索引:\(drop\ index\ INDEX\_NAME\). 可以定期重建索引以提高检索速度。
数据类型 | 含义 |
---|---|
char(n) | n位定长字符串 |
varchar(n) | 变长字符串,最大长度为n |
int | 整数,具体大小与机器有关 |
numeric(p, d) | 定点数,有p位数字,其中d位是小数位 |
real, double precision | 浮点数,双精度浮点数,精度与机器有关 |
float(n) | 精度至少为n的浮点数 |
date | 日历时间,年(4位)月日 |
time(p) | 当天时间,时分秒。秒精确到p位小数,默认为0 |
timestamp(p) | date和time(p)的组合,p默认为6 |
clob(s), blob(s) | 字符大对象,二进制大对象,s可能是x KB, MB, GB 实际存储偏移量,是大对象定位器 |
授权:\(grant\ P\ on\ TABLE\ to\ USER\) 撤回授权:\(revoke\ P\ on\ TABLE\ from\ USER\)
P是权限,包括select、insert、update、delete、all;也可以通过\(P(a)\)单独修改用户在属性a上的权限
创建角色:\(create\ role\ ROLE;\ grant\ P\ on\ TABLE\ to\ ROLE;\ grant\ ROLE\ to\ USER;\ grant\ ROLE\ to\ ROLE‘;\)
程序设计语言访问数据库的三种方式
- 动态SQL:JDBC, ODBC(低效易用)
- 嵌入式SQL:预先写好SQL语句,嵌入到编程语言中(静态高效)
- SQL API:使用专门接口,如OCI(高效,对程序员要求高)
public static void JDBCexample(String userid, String passwd) {
try {
//装载JDBC驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//打开一个连接
Connection conn = DriverManager.getConnection(
"jdbc:oracleLthin:@db.yale.edu:1521:univdb", userid, passwd);
//创建Statement对象
Statement stmt = conn.createStatement();
try {
//执行语句
stmt.executeUpdate("insert into instructor values(‘77987‘, ‘Kim‘, ‘Physics‘, 98000)");
} catch (SQLException e) {
System.out.println("Could not insert." + e);
}
//处理结果集
ResultSet rset = stmt.executeQuery(
"select dept_name, avg(salary) from instructor group by dept_name");
while(rset.next()) {
System.out.println(rset.getString("dept_name")+" "+rset.getFloat(2));
}
//关闭连接
stmt.close();
conn.close();
} catch (Exception e) {
System.out.println(e);
}
}
嵌入式:\(EXEC\ SQL\ SQL语句\),如果用到宿主语言的变量,需要先在DECLARE区段中声明,并在变量前加:
声明游标declare cursor: SQL查询的结果是一个集合,而宿主语言中的变量一次只能存储一条记录。为了调和这一矛盾,嵌入式SQL使用游标(类似指针)来获取结果集的内容,从而将结果逐行读出。
为select子句声明游标,即用一个变量x标识该查询,此时不执行查询。当程序执行\(open\ x\)语句时,数据库执行查询并将结果存入一个临时关系中。如果查询出错,数据库会在SQL通信区的变量中存储错误诊断信息;如果查询成功,通过执行\(fetch\ x\ into\ :res1,\ :res2,\ ...(取决于结果集有多少属性)\)将结果集存入宿主语言的变量中。注意每次fetch只取出一个元组,游标自动指向下一元组。如果想得到所有结果,需要用循环实现。查询结束后,使用\(close\ x\)删除临时关系。
DDL、DCL、update、delete、insert into 都不需要使用游标。结果只有一行的select子句可以直接将结果存入宿主语言的变量中(此时若结果有多行会出错)。
触发器:一条语句,当对数据库作修改时,它自动被系统执行。设置触发器必须满足两个要求:
- 指明什么条件下执行触发器:分解为引起触发器被检测的事件和触发器执行要满足的条件
- 指明触发器执行时的动作
触发器何时用与不用:触发器可以在语句插入时检查参照完整性,也可以在执行前触发,避免非法更新、插入、删除。但触发器可能加大工作量,且触发器错误会导致语句执行失败,一个触发器也可以引发无限的触发器链。因此有其他候选方法时应该避免使用触发器。
SQL实例
第6章:关系代数
关系代数
过程化查询语言,基本运算:选择、投影、并、差、笛卡尔积、更名。关系运算的结果也是关系。
选择 选出满足给定谓词的元组。 \(\sigma _{dept\_name="Physics"}(instructor)\) \(=\ \neq\ \lt\ \gt\)
可以用$\and\ \or\ \neg\ $合并多个谓词。 \(\sigma _{dept\_name="Physics"\and salary\gt 9000}(instructor)\)
投影 返回修改过的关系,排除不符合要求的属性。\(\Pi_{ID, name}(instructor)\)
可以将选择和投影组合起来实现更复杂的运算。\(\Pi_{name}(\sigma _{dept\_name="Physics"}(instructor))\)
并 将两个集合并起来。\(\Pi_{name}(\sigma _{dept\_name="Physics"}(instructor))\cup\Pi_{name}(\sigma _{salary>9000}(instructor))\)
要使并运算\(r\cup s\)有意义,必须满足:1. r和s是同元的,即属性数量相同;
- 对所有的i,r中第i个属性的域与s中第i个属性的域相同。
差 找出一个集合中有而另一个没有的元组。\(\Pi_{name}(\sigma _{dept\_name="Physics"}(instructor))-\Pi_{name}(\sigma _{salary>9000}(instructor))\)
与并运算相同,差运算也要求同元、相容。
笛卡尔积 将任意两个关系的信息组合在一起。\(instructor\times student\)
更名 给关系代数表达式的结果赋上名字。\(\rho_xE\) 返回表达式E的结果,并为其命名为x.
也可以同时为关系中的每个属性更名,假设E是n元的,\(\rho_{x(A_1,A_2,...A_n)}(E)\)
除了基本关系运算,还有一些附加的、简化查询的运算:交、自然连接、赋值、外连接
交 找出两个集合的交集。\(\Pi_{name}(\sigma _{dept\_name="Physics"}(instructor))\cap\Pi_{name}(\sigma _{salary>9000}(instructor))\)
与差、并相同,要求同元、相容。
自然连接 对笛卡尔积的结果进行简化,要求两个关系中相同属性的值一致。\(\Pi_{name,ID}(instructor\bowtie teacher\))
赋值 将关系存入一个临时变量。\(temp1\leftarrow R\times S\)、\(temp2\leftarrow \Pi_{A_1}(temp1)\)
赋值运算不会把结果展示给用户,而是将右侧表达式结果存入左侧变量,以备后续使用。
外连接 自然连接的扩展,它会在结果中创建带空值的元组,保留自然连接中可能丢失的元组。
左外连接、右外连接、全外连接
除了上述运算,还有扩展的关系代数运算能实现更高级的查询。
除运算 设\(r(R)\)和\(s(S)\)是两个关系,且\(S\subseteq R\)(模式S中每个属性都在R中),那么\(r\div s\)是模式R-S上的关系,元组 t 在\(r\div s\)中当且仅当以下条件同时成立:
t 在\(\Pi_{R-S}(r)\)中;对于s中每一个元组\(t_s\),r中都有元组\(t_r\)同时满足\(t_r[S]=t_s[S],\ t_r[R-S]=t\).
广义投影 投影的属性可以是涉及常量的表达式。
聚集\(G\) 对值的集合使用聚集函数,\(G_{sum(salary)}(instructor)\),还有\(min, max, average, count, count_distinct\)等等
如果要对一组元组集合执行聚集函数(group by),表达式为 \(_{dept\_name}G_{sum(salary)}(instructor)\)
元组关系演算
非过程化查询语言,查询表达为 \(\{t\ |\ P(t)\}\)
instructor模式:ID, name, dept_name, salary;course模式:dept_name, cid, ID, ...
以下是几个查询的例子。
找出工资大于8000的教师的所有属性:\(\{t\mid t\in instructor\and t[salary]>8000\}\)
找出工资大于8000的教师的ID:\(\{t\mid \exist s\in instructor(t[ID]=s[ID]\and t[salary]>8000)\}\)
找出在2009年秋或2010年春开课的课程号:\(\{t\mid \exist s \in section(t[cid]=s[cid]\and s[year]=2009 \and s[semester]="Fall")\)
\(\or \exist u\in section(t[cid]=u[cid]\and s[year]=2010 \and s[semester]="Spring")\}\)
如果是找出在2009年秋开课,但2010年春不开的课程号,只需将\(\or \exist u\)改为\(\or \neg \exist u\).
找出选了生物系所有课程的学生:\(\{t\mid \exist s \in student(t[ID]=s[ID])\and \forall u \in course(u[dept\_name]="Biology"\Rightarrow \exist r \in takes(t[ID]=r[ID] \and r[cid]=u[cid])) \}\)
域关系演算
\(\{ <x_1,x_2,...x_n>\mid P(x_1,x_2,...x_n)\}\),其中\(x_1,...x_n\)是域变量,P是由原子构成的公式。
找出工资大于8000的教师的ID, name:\(\{<i,n,d,s>\mid <i,n,d,s>\in instructor \and s>8000\}\)
找出物理系所有老师名及他们教的课的课程号:\(\{<n,c>\mid \exist i,a,se,y(<i,a,se,y>\in teaches \and \exist d,s(<i,n,d,s>\in instructor \and d="Physics"))\}\)
找出在2009年秋或2010年春开课的课程号:\(\{<c>\mid \exist a,s,y,b,r,t(<c,a,s,y,b,r,t>\in section\and s="Fall"\and y="2009")\)
\(\or \exist a,s,y,b,r,t(<c,a,s,y,b,r,t>\in section \and s="Spring" \and y="2010")\}\)
找出选了生物系所有课程的学生:\(\{<i>\mid \exist n,d,tc(<i,n,d,tc>\in student)\and \forall ci,ti,dn,cr(<ci,ti,dn,cr>\in course\and dn="Biology"\)
\(\Rightarrow \exist si,se,y,g(<i,ci,si,se,y,g>\in takes)\}\)
表达式的安全性:在域关系中,不恰当的表达式可能导致结果关系变得无限大(取非可能导致结果是所有不存在于原关系中的元组)如 \(\{<i,n,d,s>\mid \neg (<i,n,d,s>\in instructor)\}\)就是一个不安全的表达式。
附加规则:表达式\(\{<x_1,x_2,...x_n>\mid P(x_1, x_2, ...x_n)\}\)是安全的,当以下条件成立:
- 表达式的元组中所有值都来自\(dom(P)\)
- 对每个形如\(\exist x(P_1(x))\)的子公式,它为真当且仅当在\(dom(P_1)\)中有某个值使\(P_1(x)\)为真
- 对每个形如\(\forall x(P_1(x))\)的子公式,它为真当且仅当在\(dom(P_1)\)中有某个值使\(P_1(x)\)为真
第78章数据库设计
E-R模型
实体-联系模型用于表示概念设计。概念模式定义了实体、实体的属性、实体间的联系、实体和联系上的约束等。
数据库设计中要避免的两个缺陷是冗余、不完整
实体 是现实世界中有别于其他对象的一个“事物”或“对象”,通过一组属性表示
实体集 是相同类型的实体的集合
联系 是多个实体间的相互关联,联系中也可以有属性
联系集 是相同类型联系的集合。实体集\(E_1,E_2,...E_n\)参与联系集\(R\)。
简单属性、复合属性、单值属性、多值属性、派生属性
非二元的联系集:
弱实体集:没有足够的属性形成主码的实体集。即实体集中可能会有完全相同的元组,无法区分。
弱实体集必须与另一个属主实体集关联,弱实体集依赖于强实体集。
E-R图设计范例:
范式
见 关系数据库设计
第10章数据存取
存储介质
- 高速缓冲存储器Cache:最昂贵 容量小 易失
- 主存储器:较小 较贵 易失
- 快闪存储器:容量大 价格低 非易失
- 磁盘存储器:长期联机数据存储
以上存储器速度递减,容量递增。下文主要讨论磁盘。
磁盘
扇区是从磁盘读出和写入信息的最小单位,块包含固定数目的连续扇区,数据在磁盘和主存之间以块为单位传输。
磁盘性能的指标主要是容量、访问时间、数据传输率、可靠性
访问时间是从发出读写请求到数据开始传输之间的时间。访问时间=寻道时间+旋转等待时间(找磁道→找扇区→读取)
寻道时间是磁盘臂重定位的时间,依赖于目的磁道距离磁盘臂的初始位置有多远。平均寻道时间是寻道时间的平均值,大约是最长寻道时间的1/2,依赖于磁盘模式。
旋转等待时间是读写头到达所需磁道后,等待访问的扇区出现在读写头下需要的时间。平均旋转等待时间是平均值,即磁盘旋转一周的时间的1/2.
数据传输率是从磁盘获得数据或者向磁盘存数据的速率。
平均故障时间是平均来说期望系统无故障连续运行的时间量(是动态变化的),硬盘寿命一般大于五年。
磁盘块访问的优化:缓冲、预读、调度、文件组织、非易失性写缓冲区、日志磁盘
为了尽量减少磁盘和存储器之间传输的块的数目,可以建立缓冲区,在主存储器中保留尽可能多的块,从而最大化要访问的块已经在主存储器中的几率。
缓冲区替换策略:尽量减少对磁盘的访问,策略有LRU, MRU等
被钉住的块:限制一个块写回磁盘的时间,例如一个块上的更新正在进行时,不允许写回磁盘
块的强制写出:为减少崩溃时丢失的内容,有时会强制将缓冲区内的块强制写回磁盘以更新内容
文件中记录的组织
- 堆文件组织:一条记录放在文件的任何地方,没有顺序,通常一个关系对应一个单独的文件
- 顺序文件组织:记录根据搜索码的值顺序存储,搜索码是任意属性或属性的集合(不必是超码)
- 散列文件组织:在每条记录的某些属性上计算散列函数,从而确定记录应放到文件的哪个块中
- 多表聚簇文件组织:在每一块中存储两个或更多关系的相关记录,能高效处理连接运算。
何时使用多表聚簇取决于数据库设计者认为的最频繁的查询类型,合理使用能明显提高性能。
数据字典
存储元数据:关系的名字、属性的名字、属性的域和长度、视图的名字和定义、完整性约束,等等
也会记录关系的组织方式、存储位置、索引的名字、被索引的关系、定义索引的属性、索引的类型,等等
索引
基本的索引类型:顺序索引、散列索引。大多数数据库会在主码上自动创建一个索引。
用于在文件中查找记录的属性或属性集称为搜索码。如果一个文件上有多个索引,就有多个搜索码。
包含多个属性的搜索码成为复合搜索码,在进行特定查找时能提高效率。
-
顺序索引
关系按照某个搜索码指定的顺序排序,称为聚集索引(主索引)。此时的搜索码经常是主码。
搜索码指定的顺序与物理存储顺序不同的索引称为非聚集索引(辅助索引)。
稠密索引:每个搜索码都有一个索引项,但在非聚集索引中必须存储指向所有具有相同搜索码值的记录的指针
稀疏索引:只为搜索码的某些值建立索引。只有聚集索引才能使用稀疏索引
稠密索引通常速度更快,但稀疏索引占用空间小,插入、删除时的开销小
多级索引:索引过大时,可以在原始的内层索引上构造一个稀疏的外层索引
-
散列索引
令\(K\)表示所有搜索码值的集合,\(B\)表示所有桶地址的集合,一个桶通常是一个磁盘块,可以存放多条记录。散列函数h是一个从\(K\)到\(B\)的函数,插入一条搜索码为\(K_i\)的记录时,计算\(h(K_i)\),然后搜索具有该地址的桶。
我们希望散列函数能够具有以下特性:分布是均匀的、分布是随机的
桶溢出:桶不足/桶偏斜,可以用溢出桶+链表处理。
B+树
第11章查询处理
查询语句→语法分析与翻译→关系代数表达式→优化→执行
计算原语:加了“如何执行”的注释的关系代数表达式
查询执行计划:用于执行一个查询的原语操作序列
查询优化:构造具有最小查询执行代价的查询执行计划,由系统完成
计算包含多个运算的表达式:
-
以适当的顺序每次执行一个操作,计算结果被物化到一个临时关系中备用;
从表达式最内层的运算开始,自底向上构造一个表达式树,不断建立临时关系,最终在根节点得到结果
物化计算中可以采用双缓冲区(一个连续执行算法,另一个写出运算结果)提高算法执行速度
-
在流水线上同时计算多个运算,一个运算的结果传递给下一个,不保存临时关系
减少查询中产生的临时文件数,消除读写临时关系的代价,提高查询的效率
需求驱动的流水线:系统不端向流水线顶端发出需要元组的请求,每当一个操作收到请求时就计算下一个元组并返回。如果操作的输入来自流水线,那么该操作也会发出请求以获得元组。系统记录目前为止返回了哪些元组。
生产者驱动的流水线:各操作不等待元组请求,而是积极地生产元组,每一个操作作为一个单独的进程或县城,处理流水线输入的元组流,并产生相应的输出元组流。
如果两个关系表达式在每一个有效的数据库实例中都产生相同的结果元组集,称它们是等价的。以下是等价规则:
-
\(\sigma\)的级联:\(\sigma_{\theta_1\and\theta_2}(E)=\sigma_{\theta_1}(\sigma_{\theta_2}(E))\)
-
\(\sigma\)交换律:\(\sigma_{\theta_1}(\sigma_{\theta_2}(E))=\sigma_{\theta_2}(\sigma_{\theta_1}(E))\)
-
\(\Pi\)的级联:\(\Pi_{L_1}(\Pi_{L_2}(...\Pi_{L_n}(E)...))=\Pi_{L_1}(E)\),其中\(L_1\subseteq L_2\subseteq ...\subseteq L_n\)
-
\(\sigma\)可与\(\times\)和\(\theta\)连接结合:\(\sigma_\theta(E_1\times E_2)=E_1\bowtie_\theta E_2\); \(\sigma_{\theta1}(E_1\bowtie_{\theta 2} E_2)=E_1\bowtie_{\theta 1 \and \theta 2} E_2\)
-
\(\theta\)交换律:\(E_1\bowtie_\theta E_2=E_2\bowtie_\theta E_1\),但会导致属性顺序不同
-
\(\bowtie\)结合律:\((E_1\bowtie E_2)\bowtie E_3=E_1\bowtie (E_2\bowtie E_3)\)
-
\(\theta\)结合律:\((E_1\bowtie_{\theta 1} E_2)\bowtie_{\theta 2\and\theta 3} E_3=E_1\bowtie_{\theta 1\and\theta 2} (E_2\bowtie_{\theta 3} E_3)\),笛卡尔积也满足结合律
-
\(\sigma\)分配律:
\(\sigma_{\theta_0}(E_1\bowtie_\theta E_2)=(\sigma_{\theta_0}(E_1))\bowtie E_2\),其中\(\theta_0\)中的属性只涉及\(E_1\),\(E_2\)之一
\(\sigma_{\theta_1\and\theta_2}(E_1\bowtie_\theta E_2)=(\sigma_{\theta_1}(E_1))\bowtie_\theta \sigma_{\theta_2}(E_2)\),其中\(\theta_1\)属性只涉及\(E_1\),\(\theta_2\)属性只涉及\(E_2\)
-
\(\Pi\)分配律:
\(\Pi_{L_1\cup L_2}(E_1 \bowtie_{\theta}E_2)=(\Pi_{L_1}(E_1))\bowtie \theta(\Pi_{L_2}(E_2))\),其中\(L_1\in E_1\),\(L_2\in E_2\),\(\theta\)连接只涉及\(L_1\cup L_2\)的属性
还有一条,好长啊,不想记了,反正也不会考
-
\(\cup,\cap\)交换律:\(E_1\cup E_2=E_2\cup E_1\),\(E_1\cap E_2=E_2\cap E_1\)
-
\(\cup,\cap\)结合律:\((E_1\cup E_2)\cup E_3=E_1\cup (E_2\cup E_3)\);\((E_1\cap E_2)\cap E_3=E_1\cap (E_2\cap E_3)\)
-
\(\sigma\)对\(\cup,\cap,-\)的分配律:
\(\sigma_P(E_1-E_2)=\sigma_P(E_1)-\sigma_P(E_2)\),替换成\(\cup,\ \cap\)也成立
\(\sigma_P(E_1-E_2)=\sigma_P(E_1)-E_2\),替换成\(\cap\)也成立
-
\(\Pi\)对\(\cup\)的分配律:\(\Pi_L(E_1\cup E_2)=(\Pi_L(E_1))\cup(\Pi_L(E_2))\)
查询优化器使用等价规则的最小集。
数据库的目录信息:
\(n_r\)是关系r的元组数,\(b_r\)是r中元组所占的磁盘块数,\(l_r\)是r中每个元组的字节数,\(f_r\)是一个磁盘块能容纳的r中元组的个数
假设关系r的元组物理上存在一个文件中,则\(b_r=[\frac{n_r}{f_r}]\). 索引的统计信息也保存在目录里。
启发式优化:构建表达式树→下压选择→下压投影→运用交换律(执行连接之前,只保留可能用到的属性以提高效率)
许多优化器为查询优化制定一个成本预算,当超过优化成本预算时,就停止搜索最优计划,返回当前找到的最优计划。
许多应用会反复执行同一查询,因此需要计划缓存——缓存和重用查询计划。
第12章事务管理
事务是访问并可能更新各种数据项的一个程序执行单元
事务的特性
原子性A、一致性C、隔离性I、持久性D
-
原子性
由于事务执行出现故障,导致系统的状态不再能反映数据库所描述世界的真实状态,而是不一致状态。
数据库在磁盘上记录数据项的旧值,如果事务没能完成执行,数据库就从日志中恢复旧值,消除不一致。
-
隔离性
事务的隔离性确保事务并发执行后的系统状态与这些事务以某种次序一个接一个地执行后的状态等价。
-
一致性
对于“从账户A转账到账户B”这一操作而言,一致性要求A, B的账户之和不变。如果原子性或隔离性遭到破坏,一致性也会被破坏。
-
持久性
一旦事务成功地完成执行,系统必须保证任何故障都不会引起这次操作的数据丢失。
事务的执行状态:中止(没能成功执行)、已回滚(执行一个补偿事务,撤销变更)、已提交(成功完成执行)
当事务执行完最后一条语句,会进入部分提交状态。此时实际输出可能仍驻留在主存中,硬件故障可能会导致中止。
事务隔离性与串行化
允许并发的理由:提高吞吐量和资源利用率、减少等待时间和平均响应时间。
并发控制机制:数据库控制事务之间的交互,使其并发执行,又不破坏数据库的一致性。
可串行化:并发执行的调度在某种意义上等价于一个串行调度,效果与没有并发时的效果一样。
-
冲突可串行化
对于两条连续指令I, J,考虑以下4种情形:
- I=read(Q), J=read(Q),因为都是读,I与J的次序无所谓
- I=read(Q), J=write(Q),因为J修改了Q的值,所以I与J的顺序是重要的
- I=write(Q), J=read(Q),与2类似,I与J的顺序是重要的
- I=write(Q), J=write(Q),I与J的顺序对I和J无所谓,因为它们都写入新的值,但会对下一条read指令有影响
综上所述,当I与J是不同事务对相同数据项(Q)的操作,且其中至少一个是write指令时,I与J是冲突的
如果调度S可以经一系列非冲突指令转换成S‘,称S与S‘是冲突等价的。若调度S与一个串行调度冲突等价,则称调度S是冲突可串行化的。
-
视图可串行化
可恢复性
并发控制
-
基于锁的协议(悲观)
共享锁:lock-S(Q),获得该锁的事务能读Q但不能写Q,允许其他共享锁并存
排他锁:lock-X(Q),获得该锁的事务能读写Q,不允许其他共享锁或排他锁并存
要访问数据Q,事务必须先根据自己对Q进行的操作申请锁,在并发控制管理器授予所需锁后继续操作。
如果访问的数据项已经被其他事务加上了排他锁,当前事务只能等待,直到排他锁全部释放。
如果两个事务都在等待对方释放锁而无法进行,称为死锁,此时系统必须回滚两个事务中的一个。
封锁协议:规定事务何时对数据进行加锁、解锁,从而限制可能的调度数目。
两阶段封锁协议:能保证可串行性,不能防止死锁。要求每个事务分两阶段提出加锁和解锁申请:
增长阶段,事务可以获得锁,但不能释放锁。增长阶段的结束点称为事务的封锁点。
缩减阶段,事务可以释放锁,但不能获得锁。
严格两阶段封锁协议:除上述要求外,还要求事务持有的排他锁必须在事务提交之后才能释放
强两阶段封锁协议:要求事务提交之前不得释放任何锁
多粒度:将数据项组织成树形结构,加锁时通过封锁一个点来隐式地封锁这个节点的全部后代。由此引入了意向锁,当一个节点加上意向锁,其所有祖先节点都会被显式加锁。
-
基于时间戳的协议(悲观)
预先选定事务的顺序,从而实现事务可串行化。协议同时保证无死锁,但可能导致饥饿。
对于每个事务\(T_i\),为其分配唯一固定的时间戳\(TS(T_i)\). 越早进入系统的事务,时间戳越小。可以利用系统时钟或逻辑计数器实现时间戳机制。
对于每个数据项Q,W-timestamp(Q)表示成功执行write(Q)的所有事务的最大时间戳,下称WTS(Q);R-timestamp(Q)表示成功执行read(Q)的所有事务的最大时间戳,下称RTS(Q);这些时间戳随事务的执行不断更新。
时间戳排序协议保证任何有冲突的读写都按时间戳顺序执行,具体如下:
情形一:事务\(T_i\)发出\(read(Q)\).
- 若\(TS(T_i)<WTS(Q)\),拒绝操作,事件回滚
- 若\(TS(T_i)\ge WTS(Q)\),执行操作,\(RST(Q)\)被更新为\(RST(Q),TS(T_i)\)间的最大值
情形二:事务\(T_i\)发出\(write(Q)\).
- 若\(TS(T_i)<WTS(Q)\),或\(TS(T_i)<RTS(Q)\),拒绝操作,事件回滚
- 其他情况,执行操作,\(WTS(Q)\)被更新为\(TS(T_i)\).
如果事务被回滚,系统会赋予它新的时间戳并重新启动。
-
基于有效性检查的协议(乐观)
将每个事务的生命周期依次分为三个阶段:读阶段、有效性检查阶段、写阶段。不同事务的三阶段可以交叉进行。
有效性测试使用时间戳:\(Start(T_i), TS(T_i), Finish(T_i)\),分别对应三个阶段,其具体测试如下:
任何满足\(TS(T_k)<TS(T_i)\)的事务\(T_k\)必须满足以下两者之一:、
- \(Finish(T_k)<Start(T_i)\)
- \(T_k\)所写数据项集与\(T_i\)所读数据项集不相交,且\(Start(T_i)<Finish(T_k)<TS(T_i)\).
有效性检查机制自动预防级联回滚,但可能导致饥饿。
乐观机制得名于事务乐观地执行,假定它们能完成执行且最终有效;相反,悲观机制在检测到一个冲突时就会强制回滚,即使该调度还存在冲突可串行化的可能。
故障
事务故障(逻辑错误、系统错误)、系统崩溃、磁盘故障