栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 其他

数据库原理和应用

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

数据库原理和应用

目录

课程简介:

第一章 数据库系统简介

1.1、The class introduction

1.2、Basic concepts of database

 第二章 数据模型

2.1、层次数据模型

2.2、网状数据模型 

2.3、关于数据模型

2.4、关于键的约束问题

第三章、关系代数

3.1、概述

3.2、选择操作

3.3、投影操作

3.4、集合操作

3.5、连接操作

3.6、除操作

3.7、外连接操作

3.8、外并操作

第四章、关系演算

4.1、概述

4.2、元组关系演算

4.3、域关系演算

4.4、对传统数据模型的评价

4.5、E-R数据模型

4.6、扩充E-R数据模型

4.7、面向对象数据模型

4.8、基于逻辑的数据模型

第五章、关系数据库语言

5.1、SQL简介

5.2、查询语句基本结构

5.3、查询和操作

5.4、嵌入式SQL

第六章、数据库管理系统

6.1、DBMS核心的模块结构

6.2、DBMS进程结构

6.3、数据目录

6.4、作业

第七章、数据库的存储结构

7.1、数据库访问管理-文件组织

 7.2、数据库访问管理-索引技术

7.3、多键查询(研究生)

7.4、动态散列(研究生)

7.5、存储系统的发展

7.6、作业

第八章、查询处理和优化

8.1、查询优化概述

8.2、代数优化(后面还会更新详细笔记)

8.3、操作优化

8.4、连接操作实现和优化 

8.5、选择操作实现和优化(非考察重点)

8.6、作业

第九章、事务管理

9.1、恢复机制概述

9.2、事务和日志

9.3、更新策略以及故障后的恢复

9.4、易地更新恢复技术(研究生)

9.5、并发控制概述

9.6、并发控制的正确性准则

9.7、封锁法以及锁协议

9.8、死锁和活锁

9.9、死锁检测和死锁预防

9.10、多粒度封锁(研究生) 

9.11、索引并发控制(研究生)

9.12、幽灵及其防止(研究生)

9.13、事务隔离等级(研究生)

9.14、时间标记并发控制技术(研究生)

9.15、乐观并发控制技术(研究生)

9.16、作业

第十章、安全性和完整性约束

10.1、概述

10.2、统计数据库的安全

10.3、完整性约束

 第十一章、触发子和主动数据库系统

11.1、引论(研究生)

11.2、数据库的更新与引用完整性(研究生)

11.3、触发器(研究生)

11.4、触发器的应用(研究生)

第十二章、数据库设计

12.1、数据依赖

11.2、关系模式的规范化



课程简介:

        所用书籍为东南大学王能斌老师出版的《数据库系统教程.第二版》为教材,由于是学生所写,所以内容可能有误,欢迎指正。


第一章 数据库系统简介

1.1、The class introduction

 The textbook is our book in this course .  In this course ,we will learning this contents .

 In fact , in the course , we will refer to learn these book in under .

 Let’s introduce the content of the course .


1.2、Basic concepts of database

 Frist , we will said that what is Database . Database is a very large , integrated conection of data. It is a models real-world enterprise and it is divided into two parts , the first part which is a entities , such as we talk about the students and courses which is the entities , the second part which is the relationships , such as electives , We describe an entity in the real world and the connection between them in an appropriate way, and the resulting data set is placed in one place, this place is called a database . At this point , we is used to describ the real world method , which is called datamodel . So what is DBMS ? DBMS actually is a software package designed to store and manage databases . In this time may feel weird in some students . A file can be store data as well . In fact , the file just  is a Character stream on the operating system only have some of the most basic functions . For example creat , open , read , write , lseek(指针) so on . The application must stage large datasets between main memory and secondary storage and you must write some special code for different queries(疑问) and also Must protect data from inconsistency due to multiple concurrent users . In practical applications in life , you also can crash recovery in database . When you face emergencies such as power outages(停电) , you can be thank for the operate which crash recovery in database . So say so much  above , why use a DBNS? Because DBNS have  the data independence and  the efficient access . And then it also can reduced application development time  and  help you do a lot of low-level logic work , otherwish , DBNS also support data integrity and security , and uniform data administration .


 第二章 数据模型

2.1、层次数据模型

层次数据模型的提出,首先是为了模拟这种按层次组织起来的事物。

什么叫记录和字段?

记录(假如有个学生表):

学生名

学号

班级

年龄

生日

那么我们称上述的为记录(的)型。

其中学生名或者学号或者班级等我们称为字段。

学生名

学号

班级

年龄

生日

baka爱

15

9年一班

15

2000.1.25

黄色部分为记录的实例,我们简称记录。

而构成记录的baka爱这个学生名,我们称为数据项,在一些其他的地方也有人叫做数据类型变量。

什么是PCR?

这里的PCR不是高中生物课本的PCR,而是指双亲子女关系(Parent-Child Relationship,PCR),他是层次模型中最基本的数据关系,他代表了两个记录型之间一对多的关系。例如系和班,一个系里有很多个班。

层次数据模式长什么样?

由PCR可知,从最开始出发,一对多往下发散,那么层次数据模型应该是一棵树。

因此,除了根,每个记录型都应该有他自己对应且唯一地父母,但是每个父母可以有多个子女。

何为虚(拟)记录?

其实抛开这个问题不讲,在现实世界中,不是说所有的关系都是一对多的,也有可能是多对一,或者多对多,假设我们有这么一个例子,一个学生可以选择多门课,一门课可以被多个学生选择。假设学生是父,那么课就是子,多门课只能对应一个父,也就是说想要一门课要想能被多个学生选择,那么他必须复制多份一模一样的课出来,如图所示。

问题的关系:

用PCR处理后:

所以上述的情况直接导致了一个问题就是,明明不需要三个人工智能,却复制了三份,造成了数据冗余。

再比如上面说到的多对一关系。

为了符合子女们只能有一个双亲,所以学生得复制一份。

还有一种情况是:多元关系,PCR只表示两个记录型之间1:N,而现实中有三个记录型之间的关系,例如:

经过PCR后:

由此可知,PCR会导致数据冗余,不但浪费存储空间,还导致数据不一致。所以我们这时候就回到了上面的问题,什么是虚记录

虚记录,其实就是如果遇见上述的关系,我们无须复制一份,只需要在引用的地方用其指针来代替即可,用指针替代的记录叫做虚记录,虚记录通常用下标V表示,指针用虚线箭头表示。

例如学生和课程的关系:

由图可知,学生指向课程,但是指过去无法指回来,所以我们指向他的虚记录,虚记录指回本体,课程本体如果指向学生,那学生无法指回课程,所以课程本体指向学生的虚记录,学生虚记录指向学生本体。

同理:

运动队和班级都指向学生,那么只需要班或者运动队其中一个指向学生的虚记录,学生的虚记录再指回本体即可。


2.2、网状数据模型 

网状数据模型也以记录为数据的存储单位。

这里复习一下记录

记录(假如有个学生表):

学生名

学号

班级

年龄

生日

那么我们称上述的为记录(的)型。

学生名

学号

班级

年龄

生日

baka爱

15

9年一班

15

2000.1.25

黄色部分为记录的实例,我们简称记录。

而构成记录的baka爱这个学生名,我们称为数据项,在一些其他的地方也有人叫做数据类型变量。

记录里面只能有一个值吗?

不一定,例如还是学生这张表

学生名

学号

班级

年龄

生日

地址

baka爱

15

9年一班

15

2000.1.25

{(酒店),(饭店)}

这个学生的地址可能有两个,一个是他家,一个是他爸妈工作的地方,那么我们称这个数据项为多值数据项,可以用有序的集合来表示,简单的多值数据项我们称为向量

记录在数据结构里面有没有对应的地址?

有,每个记录有一个唯一地标识他的数据库码(Database Key,DBK)。DBK可以看成记录的逻辑地址,可做记录的替身或用来寻找记录。

网站数据结构中的系(set)是什么?

所谓的系就是联系的意思,一般来说,系代表两个记录型之间1:N联系。

比如:

班这个表和学生这个表,其中班和学生有关系,一个班包含多个学生,我们称“班级-学生”的系为他们之间的联系。

我们通常叫1为首记录,N为属记录。

体现在上面的案例上就是班级是首记录,学生是属记录。

在一个系的属记录里,如果有多种类型的属记录,我们叫做多属系

我们可以这么理解,还是上面这个例子,如果我们把学生细分,可以分为男学生和女学生,那么我们这时候“班级-学生”这个系就构成了一个多属系。

多属系优点是查找方便。

关于系的首记录和属记录几个注意的:

在层次数据模型中,我们有说到虚记录,在网状数据模型中,我们不需要考虑虚记录,因为他并没有PCR关系,就拿下面这个例子来说:

班和学生构成一个系,运动队和学生构成一个系,学生既是“班-学生”的属记录,也是“运动队-学生”的属记录,无须用PCR复制,无须用指针创建虚记录。

  • 一个记录型可以作为几个系的首记录,也可以作为几个系的属记录。

比如说:

“班级-学生”,“家庭-学生”和“学生-选课”这三个系中。

学生作为中转站,他可以做多个系的属纪录,也可以做另外多个系的主记录。

  • 一个记录型可以作为一个系的首记录,也可以作为另一个系的属记录。
  • 但是!一个记录型不能做一个系的首记录,又做这个系的属记录。

即:

“班级-学生”,你不能说学生又是属记录又是主记录。

或者再举一个例子:

职员和领导,职员是领导的下属,但是领导也是职员,也就是说在领导也在职员记录型里面,这显然是不符合要求的。

所以,我们在这里引入了一个叫做联系记录的概念。

什么是联系记录?

比如职员和领导的关系,我们中间引入一个link变量,来接收职员的箭头,再把箭头指向领导。

这样就可以避免同一记录型的自身联系。

这里要插入一个点,联系记录还有一个用法。由于一个记录值不能出现在同一系型的多个系值中,

所以我们可以如图所示:

其实看到上图我们可以知道,为了避免课4被两个学生用同一个记录,所以我们将每个课和link挂钩,用两个link来当课4的中转站。这样相当于是层次数据模型中的多元关系。

网状数据模型长什么样?

对比于层次数据模型,用班-学生的关系来说,如果是层次数据模型,那么就会是下图:

如果是网状,那么他采用的不是树的形式,而是链表的形式:

在这里要提到一个,链表用指针串联也是有讲究的,我们一般分为三类,前向指针后向指针首记录指针,其中前向指针是必备的。

前向指针:就是依次从根开始,依次指向“下”一个记录。

后向指针:和前向指针相反。

首记录指针:属记录指向首记录。

单值系无首系

实际上,两个名词指的是同一个东西,这种系通常没有首记录,所以叫无首系,或者具体点说,一个单位里面有很多部门,但是单位只有一个,我们可以直接省略,所以无首系又叫做单值系。

再比如“班级-学生”,班级有很多个,我们不能说“班级-学生”是单值系,但是“学校-班级”中的学校只有一个,总而言之,单值系和无首系都是相对的,要视具体问题来看。


2.3、关于数据模型

关系数据模型是以集合论中的关系概念为基础发展起来的数据模型。

拿个表出来做示范先:

+-------+----------+-----------+

| ename | max(sal) | job       |

+-------+----------+-----------+

| SCOTT |  3000.00 | ANALYST   |

| SMITH |  1300.00 | CLERK     |

| JonES |  2975.00 | MANAGER   |

| KING  |  5000.00 | PRESIDENT |

| ALLEN |  1600.00 | SALESMAN  |

+-------+----------+-----------+

这个表是员工表,这个写了三个字段,对应三个员工特征,我们一般把这种特征叫做属性

属性的值可以在一个定义域里面取到,比如说ename里面可以是合法姓名的集合,job可以是所有工作的集合,我们一般把属性值的定义域叫做属性的域。

这里要说明一下:数学里面有些域是无穷大或者无穷小的,在计算机里,数据都是离散的,长度都是有限的,所以属性的域可以看成是有限的集合。

域有无其他限制呢?

这就要分成两个年代来讲了。

在传统关系数据模型里,域是不允许再分的,即所有域都得是原子数据,比如是int,String,double,不可以是{(int),(String)}这种组合数据,如数组({1,2,3,4})、集合({1,a,b})、记录等等,你可以理解为不允许表中套表。但是之所以这样做是因为图简单,并不是一定不能这么做。这种比较传统的我们叫做遵守第一范式的传统关系数据模型,当然,有特殊需要时,我们可以选用不遵守第一范式的扩充关系数据模型。当然,这些都是后话,这里稍微提一下。

让我们看下面这个表

在这个表里面,COMM里面有几个是NULL,我们说关系数据模型他是有条件地允许使用空缺符NULL的,记住NULL不是一个值,他只是说明这个地方没有东西,不是0。

关系和元组

+-------+----------+-----------+

| ename | max(sal) | job       |

+-------+----------+-----------+

| SCOTT |  3000.00 | ANALYST   |

| SMITH |  1300.00 | CLERK     |

| JONES |  2975.00 | MANAGER   |

| KING  |  5000.00 | PRESIDENT |

| ALLEN |  1600.00 | SALESMAN  |

+-------+----------+-----------+

还是这个表,这里是个员工表,有三个属性,假设一个对象有n个属性,我们把n的个数叫做关系的目,如上图,员工表的关系的目是3。

关系是什么呢?一个关系里面包含所有的属性。或者通俗来讲,关系就是所有属性的集合。如上图,这是员工的关系,一个对象可以用一个或多个关系来描述。

不同属性是可以有相同的域的,比如出生年份和生日,他们的域都是有限集合的时间,但是虽然域相同,但是他们所指的意思不一样。

所以,我们通常把黄色的叫做属性(属性值),粗体的叫元组,所有的内容(也就是这张表)就做关系。关系用R或r(R)表示,他是n目元组的集合。(这里可能有人会蒙,属性不是特征吗,容我们往下看)

用上表来解释就是,R是三目元组的集合。

我们用A来表示属性,用Ai表示属性名,用D表示域,用R表示关系,用v表示对象,我们可以写出以下式子。

R = (A1/D1,A2/D2,…An/Dn)或者R = (A1,A2,…,An)

R = {t1,t2,…,tm}

t =

这里也要说明一个问题,在假定的R(A1,A2)中,A1和A2在数学逻辑上是不能对调的,但是在关系数据里面是可以对调的,也就是R(A1,A2)和R(A2,A1)是等价的。

关系和表的是等价的吗?之间有什么联系和区别?

从形式来看,关系就是表,不过关系对应的表就是个简单的二维表,不允许组合数据或者表中套表。并且,关系是元组的集合,集合三大特点(无序性,确定性,互异性)中的无序性里面就说明了,关系中的元组可以互换,即上表中的SCOTT和SMITH互换一下,完全不影响。互异性说明了元组一定是不同的。所以,关系就是个加了限制的表。

元组和属性。

我们看回这张表

+-------+----------+-----------+

| ename | max(sal) | job       |

+-------+----------+-----------+

| SCOTT |  3000.00 | ANALYST   |

| SMITH |  1300.00 | CLERK     |

| JonES |  2975.00 | MANAGER   |

| KING  |  5000.00 | PRESIDENT |

| ALLEN |  1600.00 | SALESMAN  |

+-------+----------+-----------+

每个属性都是每个列的列名,除了属性名外,即黑体部分,总共是五个元组,所以我们习惯性地叫属性叫做列,元组叫做行,元组可以看做是一个对象在关系上的实例。

有时候我们取某个元组的其中一些属性,这一些属性的子集我们叫做关系在这些属性上的投影,表示R[X]或者t[X],其中X∈[A1,A2,…,An]。

键?

这里就要提到好几个概念了,我们用一堆例子来解释这几个概念。

候选键?

含义:候选键也叫键,通俗来讲他是某个属性或属性组(多个属性),通过这个属性我们可以唯一地确定某一个元组。

范例:

mysql> select* from emp;

+-------+--------+-----------+------+------------+---------+---------+--------+

| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |

+-------+--------+-----------+------+------------+---------+---------+--------+

|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |

|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |

|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |

+-------+--------+-----------+------+------------+---------+---------+--------+

说明:在这个表中,EMPNO代表员工的编号,确定了这个编号,基本上这个员工的其他信息就确定了,但是如果你选ENAME,你并不能保证员工的名字不会重复,所以,我们叫EMPNO为候选键(键),而员工名不是。

注意:

1、候选键至少有一个,可以有多个。拥有多个候选键的时候,在里面挑一个作为主键,其他作为候补键(这里不是候选哦,是候补哦,看好字)。

2、候选键的真子集不能包含候选键。

超键?

还是上面的例子,EMPNO可以唯一确定一条元组,但是{EMPNO,ENAME}也可以,但是需要说明的是,他不符合候选键的真子集不能包含候选键这个性质,这种属性组合我们一般有个名字给他,叫做超键。

主键?

拥有多个候选键的时候,在里面挑一个作为主键,其他作为候补键(这里不是候选哦,是候补哦,看好字)

全键?

如果一个主键含有所有的属性,那么他就是全键。

外键?

用这个关系1(表)里的某个键,可以引用(寻找)其他关系(表)中的一条元组,即可以引用其他表的主键,那我们叫这个表1里的键叫外键,当然另外一个表里的主键也是外键

范例:

mysql> select empno,ename from emp;(表1)

+-------+--------+

| empno | ename  |

+-------+--------+

|  7369 | SMITH  |

|  7499 | ALLEN  |

……

+-------+--------+

mysql> select empno,job from emp;(表2)

+-------+-----------+

| empno | job       |

+-------+-----------+

|  7369 | CLERK     |

|  7499 | SALESMAN  |

+-------+-----------+

说明:通过表一的empno,我们可以找到表2的对应的其他属性的值,并且,empno在表2担任主键,所以我们称两张表的empno为外键。

注意:外键实际上是一个逻辑指针,也就是表1的empno通过表2empno的指针(地址)去寻找对应的元组,所以,指针是必须非空的,即empno必须非空。

主属性?

候选键包含的属性就叫主属性。

非主属性?

不包含候选键的就是非主属性


2.4、关于键的约束问题

一、域完整性约束

简而言之,你的属性值必须得是域中的值。比如说,学号是int类型的,你把它的值填了个char的,一定会报错。

二、实体完整性约束

每个关系都应该有一个主键,每个元组的主键的值都是唯一地,主键的值不能为NULL,否则无从区别和识别元组。


第三章、关系代数

3.1、概述

关系数据模型中提供了一系列操作的定义,这些操作叫做关系代数操作,简称关系操作。

关系操作一般包含五个基本操作,如果一个系统或者数据库产品包含这五个基本操作,那我们认为认为这个系统的功能是完备的。

任何的操作都可以通过这五个操作来组合完成。

这五个操作如下:

select operation选择操作휎

project operation投影操作π

Cross product笛卡尔乘积⋈

Set - diffrent集合差操作−

union 集合并操作∪

这里要说明一下,⋈实际上是连接的符号,但是笛卡尔乘积可以看成是连接的特例,故此表示。

事实上,目前流行的关系数据库中,除了满足关系完备性以外,还增加了不少关系代数所不支持的操作,如排序、分组、聚集函数,甚至是传递闭包的计算。


3.2、选择操作

选择操作,实际上就是加限制条件来选元组,他是一种一元关系操作。

例如:

mysql> select* from emp;

+-------+--------+-----------+------+------------+---------+---------+--------+

| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |

+-------+--------+-----------+------+------------+---------+---------+--------+

|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |

|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |

|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |

|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |

|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |

|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |    NULL |     10 |

|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |

|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 5000.00 |    NULL |     10 |

|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |

|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |

|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |

|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |

|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |    NULL |     10 |

+-------+--------+-----------+------+------------+---------+---------+--------+

说明:这是完整的一张表,现在我们要找出表中符合工资为1500到3000的元组。

mysql> select* from emp where sal>=1500&&sal<=3000 order by sal;

说明:找出表中符合工资为1500到3000的元组,按照升序排列。

+-------+--------+----------+------+------------+---------+--------+--------+

| EMPNO | ENAME  | JOB      | MGR  | HIREDATE   | SAL     | COMM   | DEPTNO |

+-------+--------+----------+------+------------+---------+--------+--------+

|  7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 |   0.00 |     30 |

|  7499 | ALLEN  | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 |     30 |

|  7782 | CLARK  | MANAGER  | 7839 | 1981-06-09 | 2450.00 |   NULL |     10 |

|  7698 | BLAKE  | MANAGER  | 7839 | 1981-05-01 | 2850.00 |   NULL |     30 |

|  7566 | JONES  | MANAGER  | 7839 | 1981-04-02 | 2975.00 |   NULL |     20 |

|  7788 | SCOTT  | ANALYST  | 7566 | 1987-04-19 | 3000.00 |   NULL |     20 |

|  7902 | FORD   | ANALYST  | 7566 | 1981-12-03 | 3000.00 |   NULL |     20 |

+-------+--------+----------+------+------------+---------+--------+--------+


3.3、投影操作

投影是一元关系操作,投影就是选取关系中的某些列,即选取关系的某些列。

比如:

mysql> select* from emp;

+-------+--------+-----------+------+------------+---------+---------+--------+

| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |

+-------+--------+-----------+------+------------+---------+---------+--------+

|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |

|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |

……

+-------+--------+-----------+------+------------+---------+---------+--------+

说明:这是一整张表,但是我现在只对这个表中员工的名字和员工的编号感兴趣。

mysql> select empno,ename from emp;

+-------+--------+

| empno | ename  |

+-------+--------+

|  7369 | SMITH  |

|  7499 | ALLEN  |

|  7521 | WARD   |

……

+-------+--------+

说明:这时候我只选取了某些属性来看,即选取了某些列,这就是投影。

这里有几个需要注意的点:

1、投影后的表由于有可能不包含候选键,这就会导致有些元组可能是重复的,这时候系统会自动消除重复元组,所得关系(表)的元组数小于原关系的元组数。如果投影后的表包含候选键,那么元组数和投影前一样。

2、在实际的数据库产品中,数据库投影后他是不会自动去重的,因为数据库他不能保证用户对重复的数据是否有应用价值,所以,除非你要求,不然他不会自动去重。

案例:统计岗位的数量?

mysql> select* from emp;

说明:这是一整张表,我们可以看到很多员工的岗位都是重复的。

+-------+--------+-----------+------+------------+---------+---------+--------+

| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |

+-------+--------+-----------+------+------------+---------+---------+--------+

|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |

|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |

|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |

|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |

|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |

|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |    NULL |     10 |

|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |

|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 5000.00 |    NULL |     10 |

|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |

|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |

|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |

|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |

|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |    NULL |     10 |

+-------+--------+-----------+------+------------+---------+---------+--------+

mysql> select count(distinct job) from emp;

说明:我们要统计岗位有多少种,所以我们要先投影后去重再用分组函数进行统计。

+---------------------+

| count(distinct job) |

+---------------------+

|                   5 |

+---------------------+


3.4、集合操作

集合操作就是三个:

并、交、差和笛卡尔乘积

首先先说笛卡尔乘积:

在表的连接查询方面有一种现象被称为:笛卡尔积现象(笛卡尔乘积现象)

下面我们验证下连接查询的原理:

mysql> select ename,dname from emp,dept;

+--------+------------+

| ename  | dname      |

+--------+------------+

| SMITH  | ACCOUNTING |

| SMITH  | RESEARCH   |

| SMITH  | SALES      |

| SMITH  | OPERATIONS |

| ALLEN  | ACCOUNTING |

| ALLEN  | RESEARCH   |

| ALLEN  | SALES      |

………………………

+--------+------------+

共56条记录

笛卡尔积现象:当两张表进行连接查询的时候,没有任何条件进行限制,最终的查询结果条数是两张表记录条数的乘积。

并交差操作:

其实这个操作和数学上面的定义是一样的,但是他在数据库里面有限制。这里就要引出一个概念叫做并兼容限制。

并兼容限制:参与并交差的两个关系的元组必须限制为同类型,也就是相同目,对应的属性的域也相同。


3.5、连接操作

连接操作是一个二元关系操作。

连接其实和笛卡尔乘积很像,连接实际上就是一个加了条件的笛卡尔乘积,他只找出那些满足条件的元组。

案例:要求每个员工的工资等级,要求显示员工名、工资、工资等级。

首先先把员工表的员工名和工资拿出来:

mysql> select ename,sal from emp;

+--------+---------+

| ename  | sal     |

+--------+---------+

| SMITH  |  800.00 |

| ALLEN  | 1600.00 |

| WARD   | 1250.00 |

| JONES  | 2975.00 |

| MARTIN | 1250.00 |

| BLAKE  | 2850.00 |

| CLARK  | 2450.00 |

| SCOTT  | 3000.00 |

| KING   | 5000.00 |

| TURNER | 1500.00 |

| ADAMS  | 1100.00 |

| JAMES  |  950.00 |

| FORD   | 3000.00 |

| MILLER | 1300.00 |

+--------+---------+

然后再找出工资等级表;

mysql> select * from salgrade;

+-------+-------+-------+

| GRADE | LOSAL | HISAL |

+-------+-------+-------+

|     1 |   700 |  1200 |

|     2 |  1201 |  1400 |

|     3 |  1401 |  2000 |

|     4 |  2001 |  3000 |

|     5 |  3001 |  9999 |

+-------+-------+-------+

拿史密斯来说,如果不加任何限制条件,那么两张表会做笛卡尔乘积,当你拿了史密斯之后,史密斯的薪资只有在工资等级的最底层,所以找表的时候,史密斯只能匹配下表的第一行。

mysql> select

    -> e.ename,e.sal,s.grade

    -> from

    -> emp e

    -> (inner)join

    -> salgrade s

    -> on

    -> e.sal between s.losal and s.hisal;

+--------+---------+-------+

| ename  | sal     | grade |

+--------+---------+-------+

| SMITH  |  800.00 |     1 |

| ALLEN  | 1600.00 |     3 |

| WARD   | 1250.00 |     2 |

| JONES  | 2975.00 |     4 |

| MARTIN | 1250.00 |     2 |

| BLAKE  | 2850.00 |     4 |

| CLARK  | 2450.00 |     4 |

| SCOTT  | 3000.00 |     4 |

| KING   | 5000.00 |     5 |

| TURNER | 1500.00 |     3 |

| ADAMS  | 1100.00 |     1 |

| JAMES  |  950.00 |     1 |

| FORD   | 3000.00 |     4 |

| MILLER | 1300.00 |     2 |

+--------+---------+-------+

实际上,根据连接的条件,我们可以分为以下的连接:

内连接

等值连接

非等值连接

自连接

外连接

左外连接(左连接)

右外连接(右连接)

全连接(很少见)

而每个条件的普遍形式我们都可以表示为:

AiθBj

其中Ai是R表的一个属性,Bj是S表的一个属性,他和Ai对应,θ是运算符的集合(包括加减乘除大于小于不等于等),这种连接我们通常叫θ连接。


3.6、除操作

除法操作不好用定义来解释,看下图。

比如:

在里和里,明显对应的R.3和R.4都包含了S表,所以{,}就是除法的结果。其他不含S表的可以看做结果的余数。


3.7、外连接操作

详细笔记看数据库那里的就行了,没有啥花里胡哨的。


3.8、外并操作

外并操作?

针对:针对那些不符合并兼容要求的。

结果:结果的属性是两表属性的总和,如果其他表没有另外一个表的属性,那么那一列会被填上NULL。


第四章、关系演算

4.1、概述

概念:除了用关系代数表示关系操作外,还可以用谓词演算来表达关系的操作,这称为关系演算。

和关系代数的区别?

关系代数表示关系的操作,须标明关系操作的次序,哪个操作先哪个后你要说清楚,所以以关系代数为基础的数据库语言是过程语言。用关系演算表示关系的操作,只要说明所要得到关系的结果,而不必标明操作的过程,因而他是非过程的。

分类:元组关系演算和域关系演算。


4.2、元组关系演算

元组关系演算就是以元组为变量,一般形式是:

利用关系演算是可以做关系代数的所有操作的。

案例:查询江苏籍女大学生的姓名。

{t[姓名]|t∈STUDENT AND t.性别 = '女' AND t.籍贯 = '江苏'}

元组关系演算与关系代数具有同等表达能力,也是关系完备的。用谓词演算表示关系操作时,只有结果是有限集才有意义。

也就是说,一个表达式的结果如果是有限的,我们叫做表达式是安全的。

反之,我们说他是不安全的。

例如{t|t∉STUDENT}。

宇宙中不属于学生的东西是无限的,这个表达式是不安全的。


4.3、域关系演算

域关系演算一般以域为变量。

一般形式为:

{|P(x1,x2,…x(n+m)}

这样说我们可能有点费解。举个例子:

假设现在有个表

mysql> select* from emp;

+-------+--------+-----------+------+------------+---------+---------+--------+

| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |

+-------+--------+-----------+------+------------+---------+---------+--------+

|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |

|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |

|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |

……

+-------+--------+-----------+------+------------+---------+---------+--------+

其中有很多域变量,比如sal里面,有300,500等等,但是我们现在要找一个工资为500-3000的,那么他会找出里面符合条件的,其他的舍弃掉。所以,查询的域结果是原域结果的真子集。

所以不难看出,域关系演算也是完备的,实际上这个案例和选择操作很像。

{|P(x1,x2,…x(n+m)}

在这里面的P实际上被称为原子公式

原子公式是什么?假如说∈R,我们称他为元组公式,并且,XopY,或者Xop常量都是原子公式。

op is one of <,>,=,<=,>=,!=

公式:

一个原子公式,或者否定P,p∩q,p∪q也叫公式。

∃X(p(X)),其中变量X是一个自由变量或者∀X(p(X)),其中变量X是一个自由变量,那我们也称其为一个公式。

自由变量:

自由变量是指没有被量词(∃、∀)绑定的变量。


4.4、对传统数据模型的评价

一般来说,我们把层次,网状和关系数据模型称为传统数据模型。

传统数据模型有四个弱点:

1、以记录为基础,不能很好地面向用户和应用。

比如衣服,有一个衣服的表,里面有个属性是衣服的袖口类型,但是在现实世界中有很多连袖口都没有的衣服,虽然有NULL可以作为填补,但是用NULL是迫不得已,并不是一种自然的表示。

2、不能以自然的方式表示实体之间的联系。

有些联系本来不是那种PCR关系,不是链表关系,但是我们还是要引入什么link,什么虚记录来表示这些关系,这些表示是很不自然的。

3、语义贫乏。

比如你定义了一个表,表里面有学生的性别、年龄、身高、体重,但是你却不知道他们本身代表什么意思,我可以说这是学生的医疗记录,也可以说这是入学信息报告。还有表与表直接的联系,由于没有标明联系,有时候用户还需要查帮助文档和一些文件才能了解。所以,由于语义不明,导致就算语义理解错了也要用户自己负责。

4、数据类型太少,难以满足应用需要。


4.5、E-R数据模型

E-R数据模型,也叫实体联系数据模型。

提出目的:

1、企图建立一个统一的数据模型,以概括三种传统的数据模型;

2、作为三种传统数据模型互相转换的中间概念

3、作为超脱DBMS的一种概念数据模型,以比较自然的方式模拟现实世界。

三个概念:实体,属性,联系

实体:

概念:凡是可以被人互相区别的东西就叫实体。

范例:飞机,春游,神,梦。

注意:同类实体我们可以归为一类,称为实体集

范例:用E代表学生的实体集,那么E = {e|e是学生}

属性:

概念:实体的特征叫实体的属性,属性取值的范围叫值集。值集的说法相当于关系数据模型里的属性域。

联系:

概念:实体之间(也包括和实体集)的各种关系我们称之为联系。

范例:某个学生(实体)和课程(实体)之间有选课关系。学生(实体集)和课程之间的选课关系。人与人直接有领导关系,夫妻关系。

表示方法:如果实体e1,实体e2之间有联系,我们用来表示,扩展到n个实体,我们用来表示,当n>2的时候,我们称为多元联系。

联系集:

概念:比如说夫妻是一种联系,那么具体的比如:两个人就是这个联系的实例,生活中是夫妻的不止一对,所以多个实例我们用集合包括起来,称为联系集。

有时为了表明实体在联系中的作用,我们会这么表示,以夫妻为例:

其中r表示哪个是夫,哪个是妻。

说明:联系虽然用元组表示,但实体间的次序不是重要的,尤其标明作用后,他们的次序可以任意。

E-R图?

用E-R数据模型对一个单位的模拟,称为一个单位的E-R数据模式。E-R数据模式可以用非常直观地E-R来表示。

其中矩形框代表实体,菱形代表联系,椭圆代表属性,双线矩形框框代表弱实体

弱实体是什么?

弱实体实际上可以当成实体,可以当成属性,由数据库库设计者决定。

范例:比如一个职工,他的家属实体集可能有姓名,出生日期,和职工的关系这些属性,但是家属的姓名是可以重复的。什么意思呢,简而言之就是家属实体集中的表没有键(没有实体键),导致失去了辨认家属的能力了,所以一般来说弱实体可以不做实体,只当做拥有这个实体集的那个实体(所有者实体)的组合属性。

 E-R图相关约束?

基数比约束:

比如联系有1:1,1:N,N:1,M:N。我们把这个数量上实体间的约束称为基数比约束。

范例:一个员工只能在一个工作部门工作,这就是1:1。

参与度约束:

比如这个银行卡账号你最多只能取钱5次,比如这门课最多容纳学生150人,这就是参与度约束。

表达方式:(min,max)

基数比约束和参与度约束构成联系约束。


4.6、扩充E-R数据模型

扩充E-R数据模型我们通常称为EER。

EER引入以下的几个概念:弱实体、特殊化和普遍化、聚集、范畴。

  • 弱实体(Weak entity)

这个上一节已经说过了。

  • 特殊化和普遍化(Specialization and Generalization)

举个例子你就明白了。

范例:从普遍到特殊叫特殊化,如学生是普遍,划分为研究生高中生本科生是特殊,研究生还可以继续划分为学术型研究生和专业性研究生。反之叫普遍化。

对比:和java中的继承很像

概念:

说明:全特殊化可以理解为等价,部分特殊化可以理解为就是我们口中说的特殊化。

图例:带U符号线表示特殊化,圆圈d表示不相交特殊化,圆圈o表示重叠特殊化,线上带一条线表示实体键。

  • 聚集(Aggregation)

就是把实体集和联系看成新的一个实体集,新的实体集中的属性是原来实体集的属性和联系的属性的并。

范例:

  • 范畴(Category)

在模拟现实世界时,有时候要用到不同类型的实体组成的实体集。我们把这类实体集叫做范畴。

范例:车主有可能是一个人,有可能是一个企业的单位。

表达方式:T⊆(E1,E2,E3,…,En)

说明:其中里面的不同的实体集我们叫做超实体集,例如E1是超实体集。

图例:圆圈∪代表并操作。

这里有一点需要说明一下,我们前面说过特殊化,特殊化也就是一个子类对于父类的继承,并且继承了所有的属性,但是范畴不一样。拿这个例子来说,单位和人构成范畴,但是这个账户不是说既是单位又是人的,他的继承具有选择性,如果账户是单位的,则继承单位的属性,是个人的继承个人的属性,这叫选择性继承

反观上面特殊化图的这个部分

在职进修生既是教职工,也是学生,他拥有两者的属性。


4.7、面向对象数据模型

4.8、基于逻辑的数据模型

第五章、关系数据库语言

5.1、SQL简介

什么是数据库的用户接口?

用户使用数据库时对数据库进行的操作,DBMS要想方设法满足,其提供的命令和语言就叫用户和数据库的接口。

DBMS提供多种用户接口来针对不同的适用人群。

接口包括什么?

  • 查询语言
  • 图形化工具(GUI)
  • APIs
  • 类库

什么是数据库语言?

DBMS所提供的语言一般局限于对数据库的操作,有别于计算完备的程序设计语言,称为数据库语言。

也就是说:数据库语言不是程序设计语言,不能编程,不能进行高难度的计算。

sql、DB、DBMS分别是什么?他们之间的关系?

DB:

Database(数据库,数据库实际上在硬盘上以文件的形式存在)

DBMS:

Database Managemeng System(数据库管理系统,常见的有:MySQL Oracle DB2 Sybase sqlServer。。。)

SQL:

  • 结构化查询语言,是一门标准通用的语言。标准的sql适合于所有的数据库产品。
  • SQL属于高级语言,只要能看得懂英语单词的,写出来的sql语句,可以读懂什么意思。
  • SQL语句在执行的时候,实际上内部也会进行编译,然后再执行sql(sql语句的编译由DBMS完成。)
  • SQL是一个非过程化的查询语言

学习MySQL主要还是学习通用的SQL语句,那么SQL语句包括增删改查,sql语句该怎么分类呢?

SQL按其功能可以分为四大部分:

DDL:数据定义语言,用于定义、撤销和修改数据模式,creat、drop、alter。

DQL:查询语句,凡是select语句都是DQL。

DML:用于增删改数据,insert、delete、update。

DCL:用于数据访问权限的控制。grant授权,revoke撤销权限。

TCL:commit提交事务,rollback回滚事务。


5.2、查询语句基本结构

基表和虚表:

基本是真真实实存在的,他的数据显式地存储在数据库中,或者换一种说法就是,你当时存的时候什么样基表就长什么样。

而虚表是仅有逻辑定义,可以根据其定义从其他表(包括视图)中导出,但不作为一个表显式地存储在数据库中。换一种说法就是,比如你数据库里面已经有个基表了,然后我通过某些要求过滤了一些条件,查询出来的表就是虚表,虚表实际上不存在数据库里,他只是通过一些计算和逻辑语言提取出来的。

当基表的模式修改时,通过定义适当的视图,仍可以为用户提供修改前的数据模式,避免修改应用程序,从而有利于提高数据的逻辑独立性。也就是说,即使你基表改了,但是为了视图还是和以前一样,我们可以在基表的基础上做一些其他的操作,使他改变操作后算出来的虚表和之前没改的虚表一模一样。

NULL:

代表空值,但是他不是一个值,不等于0,而是代表这里没有值。在以前我们是二值逻辑,即要么true要么false,引入NULL后,我们由二值逻辑变为三值逻辑。

NOT NULL:

不允许对应的属性值为空。

Unique:

不允许属性值重复,即列值不能重复。

DEFAULT:

如果属性值为空,则填个默认值上去。

一般默认值有三种情况:

第一种是事先定义好的字符,比如如果为空就补个0上去。

第二种是置为空格字符串,把NULL变成长度为0的字符串或者变成某一个不可能出现的值。

第三种就是变成NULL,如果你要这么做,那你 前面不能再加一个NOT NULL不然语义矛盾。

CHECK:

利用他,可以说明属性值的范围,也就是加限制条件,比如人的年龄不可能为负数,你可以加age>0。


5.3、查询和操作

这里不细讲,想要了解请前往同主页下的数据库观看。


5.4、嵌入式SQL

第六章、数据库管理系统

6.1、DBMS核心的模块结构

数据库管理系统简介:

数据库管理系统(DBMS)是数据库系统的核心,它对数据库系统的功能和性能有决定性影响。

目前商品化的DBMS主流是以关系模型或者扩充了对象概念的关系模型为基础的。

数据库操作系统实现过程

这里要对这幅图说明一些东西,从上往下操作:首先这里核心是语义分析和查询处理,操作系统是在在其之上做文件管理等操作,而在这里,接口分为两大类,UFIAPI,其中UFI是提供给用户进行即席访问的。即席访问是什么意思呢?实际上就是有个人在窗口这里不断的给数据库提供指令,如用户。

说明:从上到下,从应用开始,我们在应用里面抽出数据库语句,这个过程是交给词法及语法分析器去抽离的,然后转换为一种最基本的数据库语言,如SQL,交词法和语法器分析,产生相应的语法树。然后进行授权检查,检查用户是否有权访问语法树中所涉及的数据对象。如果授权检查通过,则继续执行,如果不通过那就驳回消息不执行。

比如他发现应用里面有数据库语句是要求要查那张表,那数据库这边会马上检查用户是否有权限去查这个表,如果没有权限即驳回。

通过授权管理后,将语法树往下继续,会经过语义分析和查询处理,一般都是用基本的SQL语句去处理,在查询处理这个地方,我们通常还存在一些存取路径的选择问题,也就是所谓的查询优化。

经过以上操作后,就形成了SQL语句的执行计划,执行也就是去做从应用传过来那些相应的操作,在操作中还需要注意并发控制DLL还有恢复机制

并发控制:SQL语句在执行的过程中,为了防止多用户一起访问数据库导致数据库数据引起不一致,所以会采取并发控制。

DLL:DLL是存放SQL函数可执行代码的动态链接库。

恢复机制:能够是数据库恢复到最近的一致。

访问原语:实际上,我们可以把这一层一层的关系看做是下一层为上一层提供了一个API,这个API函数我们叫做访问原语。

从下往上传:

首先磁盘给操作系统的只能是状态信息或者物理块,也就是说把东西交给操作系统与否,给了写TRUE,没给写FALSE,除了传递这些还有传递一些字符流。这时候物理块里面是有信息的,只是没有人去包装他,现在暂时无法解析他。但是结果物理层的加工包装,这些消息和数据可以变成表啊,元组这样的东西了,已经可以形成一些明显的概念了。再往上,物理层再度深入加工,将已知的消息包装成用户能看懂的消息了,比如本来只是一个查询语句往上就会包装成很漂亮的表格。

在这里要说明的是,并发控制并不是每个数据库系统都必须的。


6.2、DBMS进程结构

DBMS以进程为单位,在操作系统上运行的系统软件。

DBMS分类:

专为单个事务服务的DBMS核心进程(也被称为前台进程)

每个事务都有一个与之对应的DBMS的核心进程,它是事务的执行者,并代表事务与用户和系统对话。由于DBMS核心进程可以和用户直接对话,所以我们一般也叫他前台进程。

为公共服务的后台进程

后台进程无法和用户直接对话,而由DBMS调用。一般我们将一些公共操作或事务无法承担的操作由后台进程来执行,如写入数据库后更新后的内容(一般交给事务提交后执行)、预先读取可能用到的物理块、异常结束事务的善后处理等。

前台进程由于是为事务服务,所以寿命和事务一样,而后台进程是常驻进程。一般不影响事务的提交,其执行时间由系统掌控,如在适当时间成批写入数据库的更新内容

进程也是有缺点的

缺点的由来:

当你的应用软件创建了进程后,DBMS也会在前台创建一个核心进程,其中他们用管道连接,应用软件利用SQL语句发给DBMS核心进程,而核心进程发结果给应用。

但是这样的话,你会发现,一个应用连接一个DBMS核心进程,那么如果有多用户,就会导致DBMS核心进程急剧膨胀,操作系统性能下降

缺点:

1、进程的创建、撤销、切换、通信的开销较大

2、由于CPU的处理能力和内存空间的限制,计算机系统可并发运行的进程数是有限制的。

如并发的进程数过多,则进程切换频繁、系统开销增大,处理效率反而下降。操作系统一般设有并发度的限制,并且做为上限。

3、不利于事务共享内存空间。从以上的叙述我们可以知道,应用和进程是单连接的,也就是说,进程是有各自的独立内存空间,受操作系统保护,不能访问彼此的内存空间。

在数据库系统中,所有的事务共享一个DBMS,许多重要资源都是共享的,如数据目录、锁表、各种内存缓冲区。

线程?

在进程中,我们还可以创建线程,作为并发运行的调度单位。这样的话,也就是说,线程占的内存比进程小得多。

仔细来说:

线程的主要资源来源于进程,属于线程本身的专用资源很少。

描述线程的状态表要比进程的状态表容易的多。

线程之间可以通过内存通信。

线程切换的开销要比进程切换低的多,甚至不需要通过操作系统。

所以,我们通常叫线程叫做轻量进程,进程叫做重量进程

DBMS进程开启后的状态?

DBMS进程刚开启的时候,有一些东西是已经有的:

daemon线程:

起一个监控的作用,他会监视应用程序访问数据库的一些请求。比如我们和SQL的端口号连接的时候,daemon线程就在监视是否连接成功。

catalogue目录:

他是一种原数据,我们存储在数据库的数据提炼的大纲数据就是目录。比如我们写select* from emp;数据库如何查看有无这张表?就是通过目录去查找。

封锁表:

用来控制对锁的申请,他是一个公共的资源

buffer缓冲区:内存的缓冲

建立进程的过程:

说明:DAEMON实际上只负责监听进程是否建立了,一旦确定建立后他就会消失,等待下一个进程连接。


6.3、数据目录

数据目录是一组关于数据的数据,也叫元数据。

在高级程序设计语言中,程序中的数据一般容易失效。

而在DBMS的任务是管理大量的、共享的、持久数据。有关数据的定义和描述必须长期保存在系统中,一般把这些元数据组成若干表,称为数据目录,由系统管理和使用。

数据目录怎么生成的?

他是初始化的时候由系统自动生成。数据目录是被频繁访问的数据,同时也是十分重要的数据,数据目录没有建立的时候,任何SQL无法查询,无法生效,所以,数据目录影响全局。一般DBMS不会让你更新数据目录,DBMS会自动帮你更新,他只允许你查询就够了。


6.4、作业

1、如果事务不遵守ACID准则,会对数据库产生什么后果?为什么一般程序中不提ACID准则?

2、何谓线程?试着分析现代DBMS趋向采用多线程进程结构的原因。

3、试着分析当前客户/服务器系统结构和三层客户/服务器系统结构流行的技术背景。

4、试着叙述数据目录的作用、内容和管理的特点。


第七章、数据库的存储结构

7.1、数据库访问管理-文件组织

记录和文件的关系?

1、传统的数据模型都以记录为基础。记录的集合构成文件。

2、文件须按照一定的结构组织和存储记录,访问有关的记录须经由一定的存取路径。对数据库的操作最终落实到对文件的操作。

文件访问的方式?

首先这里要说一下一个概念:缓冲池和物理块

在市面上,磁盘中的数据划分为大小相等的物理块,每个物理块之间要留有间隙,间隙通常放的是初始化信息和下一个物理块的地址信息。

实际上,我们读取磁盘的时候,并不是像1kb1kb那样的字节去读取的,我们是一个物理块一个物理块去读的。所以,我们通常把磁盘叫做块设备。像linux来说,一般一个物理块是1024个字节。现在假如我们有一个关系想要放入文件当中,此时关系中一个元组占一百个字节,那么我一个物理块能放10个元组左右,那么到时候我们读硬盘的时候,就是一块一块读的。比如我们查询的时候就是一块一块找,看这一块有无我们需要的东西。

  1. 查询文件的全部或相当多的记录

一般把访问15%以上记录的查询都归入这一类。

为什么?

因为根据统计学规律,如果有15%以上的记录在硬盘的盘的话,那么我们我们可以理解为这15%几乎遍布所有的物理块,我们必须找出全部的物理块才能找到这15%的记录。

  1. 查询某一特定记录
  2. 查询某些记录

一般把访问15%以下记录的查询都归入这一类

  1. 范围查询
  2. 记录更新

比起查询操作,记录的更新要复杂的多,因为在更新的时候会引起记录位置的调整和相应的修改(比如索引就要改)

数据库对文件有什么要求?

DBMS有自己的一套文件管理系统,之所以不用我们操作系统(比如window)的那一套,主要有以下原因:

  1. DBMS为了实现其功能,须在文件目录、文件描述块、物理块等部分附加一些信息,而传统的文件系统是不提供这些信息的。
  1. 传统的文件系统主要面向批处理,而在数据库系统中,常常要求即席访问、动态修改。这就要求文件结构能够适应数据的动态变化,提供快速访问路径。
  2. 传统的文件基本上是为某一用户或者某类用户服务的,用途比较单一,共享的程度较低;而数据库中文件是供所有数据库用户共享的,甚至有些用途是不可预知的。这就要求数据库的文件结构兼顾多方面的要求,提供多种访问路径。
  3. 如果采用操作系统的文件管理系统作为DBMS的物理层的实现基础,则DBMS对操作系统的依赖不大,不利于DBMS的移植。
  4. 传统的文件一旦建立,数据量是比较稳定的;而数据库中文件的数据量变化较大,数据库中的文件结构应能适应这样的变化。

为了适应文件的访问,DBMS提供了多种文件结构

堆文件

堆文件就类似堆东西一样,不停的往尾巴放东西,不停的在尾巴后插入数据。但是堆文件不一定是顺序结构,可以是链表结构。在查找的时候我们要通过顺序扫描,即从头到尾找,而且最坏的情况是你找到最后才找到。

很明显,堆文件对于查询文件的全部或相当多的记录有相当好的效果

直接文件(哈希文件)

直接文件中,将记录的某一属性用哈希函数(在数据库里面我们以后叫做散列函数)直接映射成记录的地址,被散列的属性称为散列键。

这话可能听着憋屈,我们用一个例子说明

假如这里有个表,里面的记录我们叫元组嘛,然后我们将记录的某一属性(比如说员工编号)映射到另外一个表上,以后我们在那个表想要寻找表里的某个记录,我们就可以根据散列表里面属性对应的地址找到表中相应的元组,在这里的操作里员工编号作为我们的散列键。

索引文件(配合B+树索引)

一般我们这里指的索引指的是静态索引,但是实际应用中我们用的更多的是B+树索引,也就是动态索引。


 7.2、数据库访问管理-索引技术

数据库存储介质的特点:

讲这个之前先说一个磁盘的结构(详细资料请访问https://blog.csdn.net/heuguangxu/article/details/80072024)

说明:首先,左边有很多盘片,右边是磁头臂,下面有个两个马达,盘片马达驱动磁盘旋转,磁头臂的马达可以驱动磁头臂移动,方便读取磁盘片上的数据。

然而如果你用操作系统中的文件系统存储的话,文件在磁盘上的地址是不确定的(这里指的不是路径,是内存地址),如果是利用FAT(微软发明的文件配置表)和NTFS去寻找文件位置,他会依靠之前存取的时候存储的碎片化位置去寻找,这样就会造成寻找文件的效率变低,如果你想把文件存在同一个磁盘片中,而且物理地址连续,那么操作系统是做不到的。

摩尔定律是英特尔创始人之一戈登·摩尔的经验之谈,其核心内容为:集成电路上可以容纳的晶体管数目在大约每经过18个月便会增加一倍。换言之,处理器的性能每隔两年翻一倍。但是在磁盘中,有个固定操作叫寻道,他的过程就是将磁力壁定在磁盘片上的某条轨道(磁道)切割磁力线,从而寻找文件的地址。在这个过程中,摩尔定律显然不适用,也就是说,无法通过对硬件的升级来解决效率的问题。所以如果想提高效率问题,一定要把文件的内容根据物理地址按顺序排列好,减少磁头的移动。

每个盘片的盘面被划分成多个狭窄的同心圆环,数据就存储在这样的同心圆环上面,我们将这样的圆环称为磁道 (Track)。每个盘面可以划分多个磁道,最外圈的磁道是0号磁道,向圆心增长依次为1磁道、2磁道…磁盘的数据存放就是从最外圈开始的。

根据硬盘的规格不同,磁道数可以从几百到成千上万不等。每个磁道可以存储数 Kb 的数据,但是计算机不必要每次都读写这么多数据。因此,再把每个磁道划分为若干个弧段,每个弧段就是一个扇区 (Sector)。扇区是硬盘上存储的物理单位,现在每个扇区可存储 512 字节数据已经成了业界的约定。也就是说,即使计算机只需要某一个字节的数据,但是也得把这个 512 个字节的数据全部读入内存,再选择所需要的那个字节。

柱面是我们抽象出来的一个逻辑概念,简单来说就是处于同一个垂直区域的磁道称为柱面 ,即各盘面上面相同位置磁道的集合。需要注意的是,磁盘读写数据是按柱面进行的,磁头读写数据时首先在同一柱面内从 0 磁头开始进行操作,依次向下在同一柱面的不同盘面(即磁头上)进行操作,只有在同一柱面所有的磁头全部读写完毕后磁头才转移到下一柱面。因为选取磁头只需通过电子切换即可,而选取柱面则必须通过机械切换。数据的读写是按柱面进行的,而不是按盘面进行,所以把数据存到同一个柱面是很有价值的。

活动头磁盘的存取实际由三部分构成:寻道时间、旋转延迟实际和传输时间。活动头磁盘通过磁头臂的机械移动来寻道,寻道时间要比其他两部分时间大得多,所以为了有效地访问磁盘,我们应该尽可能地一次I/O连续访问较多的数据,减少I/O次数,从而减少寻道和选择延迟的次数。

在早期的DBMS中,常由操作系统来分配数据库所需的物理块,逻辑上相邻的物理块分散到磁盘的不同区域。在连续访问数据库中的数据时,性能严重下降,所以在现代DBMS中,都改由DBMS在初始化时向操作系统一次性申请所需的磁盘空间,让DBMS去分配。

连续分配法:有点像顺序表,即物理位置上是连续的。

链接分配法:有点像链表,也就是逻辑位置上连续。

簇集分配法:物理连续逻辑也连续。

索引分配法:每个文件都有一个逻辑块号与其物理块地址对照的索引,通过索引,你可以找到文件中任何一块的地址。

在索引里我们用的最多的索引技术:

B+树索引

簇集索引

倒向文件

动态散列

等等。。


7.3、多键查询(研究生)

7.4、动态散列(研究生)

7.5、存储系统的发展

7.6、作业

1、为什么扩大数据库系统的缓冲区可以提高其性能?

2、堆文件、直接文件和索引文件各适用于哪种场合?

3、为什么主键上一般都建有主索引

4、当查询的元组数在总元组数中所占的比重较大时,为什么用非簇集索引有时反而不利?为什么再次情况下用簇集索引有利?

5、在数据库系统中,散列一般用得不如索引多,试分析其原因。

6、如何减少B-树的级数


第八章、查询处理和优化

8.1、查询优化概述

查询优化:

用户不必关心查询语言的具体执行过程,而由DBMS确定合理的、有效地执行策略。

查询优化实际上是在做什么呢?

实际上当用户写了一条SQL语句后,查询系统会对sql语句做一个重写,然后变成一个执行效率更高的形式。

查询优化的途径

1、把查询语句进行变换,改变基本操作的次序,然后查询起来更加有效,这个叫做代数优化

2、根据系统所提供的存取路径,选择合理的存取策略,也就是对存取的方法进行优化,我们叫做物理优化

3、有限查询优化根据启发式规则和选择执行的策略去先做选择,投影等一元操作,然后做连接操作,这叫规则优化

4、在很多的优化策略里面,选取代价最小的优化执行策略,叫做代价估算优化

这里仔细讲讲什么是代数优化

假如回到小学,x=13,y=5,老师要我们计算x2+2xy+푦2

如果这里直接计算,那至少算四次乘法两次加法,而如果化为(x+y)2,那么只需要做一次加法一次乘法,效率提高了。

查询优化的注意点:

从上面这个例子我们知道,变换后求解结果的效率变快了,但是如果变换的过程很复杂,花费的代价比最开始计算还难,那也是不值得的。

查询树和语法树

数据库查询语言的处理过程和一般高级程序设计语言相仿。都是经历了解释和编译的过程。

还是这个图,应用调用接口传入命令语句,词法和语法分析器会去解释命令语句,在这里他就会用到一个语法树,将命令语句转变为SQL语句,然后授权检查是否有权限;当有权限后,就会执行sql语句,这里就涉及到查询优化的问题了,利用查询树来优化该SQL语句。


8.2、代数优化(后面还会更新详细笔记)

代数优化:

代数优化是对查询进行等效变换,以减少执行的开销。最常用的变化原则是,尽量缩短查询过程中的中间结果。由于选择、投影等一元操作分别从水平方向或垂直方向减少关系的大小,而连接、并等二元操作本身的开销较大,而且很可能产生大的中间结果。因此在变换查询时,我们让选择和投影先做,再做二元操作。

简单来说,假如现在有两张10000个元组的表,想做查询和连接两个操作,根据笛卡尔乘积现象,简单来说,假如现在有两张10000个元组的表,根据笛卡尔乘积现象,当两张表进行连接查询的时候,没有任何条件进行限制,最终的查询结果条数是两张表记录条数的乘积。也就是要做10000乘以10000次乘积,这无疑是很大的,那如果我们根据选择操作先筛选掉一部分,然后在做连接操作,那么执行效率就会大大提高。

假如我们现在有这么个查询

依据上面的sql语句画出查询树

原始查询树很容易画,只需SELECt子句对应投影操作,FROM子句对应笛卡尔乘积操作,WHERe子句对应选择操作即可。

说明:实际上,这里的查询树依据不是原始查询树了,而是经过优化的查询树,因为他避免了很多and操作。所以如果想做代数优化,就要把一元操作往树叶下面压。

下列有一些常用的变换规则

说明:这里只需要理解,不需要背,并且,一个成熟的数据库系统不止这九条规则做查询优化。

代数优化的基本步骤:

1、以select子句对应投影操作,以from子句对应笛卡尔乘积,以where子句对应选择操作,生成原始查询树。

2、应用变换规则,尽可能把选择操作往下压(树叶方向压)。

3、应用连接、笛卡尔乘积的结合律,按照小关系先做的原则,重新安排连接的次序。

4、如果笛卡尔乘积后还须按连接条件进行连接操作,可将两者组合合成连接操作。

5、对每个叶节点加必要的投影操作,以消除对查询无用的属性。


8.3、操作优化

操作优化重点考察连接操作优化。

连接操作的代价估算?

估算连接操作花费的代价,也就是估计连接结果的大小,所以我们在这里引入了一个选择因子的概念。

选择因子?

英文名join selectivity(简称js),js = |R⋈cS|/|R×S| = |R⋈cS|/|R|·|S|。

如果无连接条件C,则连接笛卡尔乘积,js=1。如果没有元组满足连接条件C。则js=0.一般地,0<=js<=1。

在连接操作优化里去估算代价我们用以下几种方法

嵌套循环法

利用索引或散列寻找匹配元组法

排序归并法

散列连接法


8.4、连接操作实现和优化 

连接是开销很大的操作,历来是查询优化考察的重点。

嵌套循环法

大体来说他像我们编程语言里面的循环嵌套一样。

假如现在有两个关系(表):R和S

说明:如果说我们要进行连接操作,本质上连接操作就是加了条件的笛卡尔乘积。那么我们是利用嵌套循环法就是先用R中的一条元组去匹配另外S表中的所有元组(S表中的所有元组相当于内层循环),然后R中的第二条元组再去匹配S中的所有元组,以此类推,直到R的所有元组和S的所有元组比较完为止。

在整个连接过程中,R只要扫描一次,S就要扫描n次(也就是S表中所有的元组),从程序设计的观点来看,R的扫描相当于程序的外循环,而S相当于程序的内循环,因此我们把R叫做外关系,S叫做内关系。当然,两张表的地位可以互换,这取决于两者扫描的IO代价。

在前面的章节中,我们曾经谈论过磁盘。

如果说R中表中一条匹配S表中一条元组就要I/O一次,那么这显然是效率极低的。但是实际上,在操作系统中硬盘是一种块设备,他是以物理块为单位,也就是说,假如一个物理块是1040M,R中一条元组为100M,那么一个物理块就可以容纳R中十条元组去匹配S表。

也就是说,我们现在设置两个缓冲区,分为外循环和内循环。

外循环的缓冲区可以存放外循环的物理块,内循环的缓冲区可以存放内循环的物理块,当两张表做连接,我们采用循环嵌套法的时候,我们不再是一条一条元组匹配,而是如上所说一个物理块匹配内循环的一个物理块,如果当你内存足够大,你可以设置多个缓冲区,存放更多的物理块,如果这张表不大,甚至能做到一波全部匹配完,大大减少了匹配效率。

我们现在设置两个缓冲区,分为外循环和内循环。

外循环的缓冲区可以存放外循环的物理块,内循环的缓冲区可以存放内循环的物理块,当两张表做连接,我们采用循环嵌套法的时候,我们不再是一条一条元组匹配,而是如上所说一个物理块匹配内循环的一个物理块。

说明:

事实上,关系不是以元组为单位,而是以物理块为单位从磁盘读取到内存。设系统分别为R,S各提供一个缓冲块,p푅为R的块因子(每块含有的元组数),则R每次IO一次不是取一个元组,而是取p푅个元组(也就是说S扫描一次可以R中的多个p푅做比较);这么说的话,假设R的物理块数是b푅,那么我们有b푅=[n/p푅],也就是物理块数等于R表总元组数除去每一个物理块所能容纳的元组数。

如果当你内存足够大,你可以设置多个缓冲区,存放更多的物理块,如果这张表不大,甚至能做到一波全部匹配完,大大减少了匹配效率

说明:

由上面的说明我们得知,用物理块去匹配S能提高效率,由此我们受到启发,如果为R增加缓冲区,那么每次多去几块物理块,是不是就可以减少S的扫描次数。

利用B+树索引或哈希索引寻找匹配元组法

细说:这里不禁要说这个算法实在牛逼啊!

具体操作是这么回事:

我们在两个表中还是那样:

R表作为外循环,S表作为内循环,然后给R表一个缓冲区,S表带有B+树索引。

之后R表一个物理块读进来,他要找S表中匹配的元组,就可以通过B+树索引找到物理块中符合连接条件的元组的地址,直接进行匹配,大大节省了寻找匹配元组的效率,不需要像嵌套循环法那样去扫描。

但是这个需要考虑代价:假如在某个属性上面,重复值的数量达到了总元组数的百分之二十以上的话,用索引反而不合算。什么意思呢?如果有过多的重复值,也就意味着你要在建树的时候有许多重复值,这就会导致在辨别重复值的时候花费过多的代价,这样还不如使用循环嵌套法来的方便。

散列连接法

具体来说就是:他把R表和S表中以后做连接有可能产生的结果全部哈希到一个柜子里,柜子有多个抽屉,不同的抽屉代表不同的结果,以后只要需要两张表的连接,只需要在哈希出来的这个柜子(表)里面去找就行了。

但是考虑到代价,也就是说你这个R表和S表不会频繁的去更新,否则的话会打乱哈希出来的表,定期做维护代价也会很大。


8.5、选择操作实现和优化(非考察重点)

8.6、作业

1、试着比较数据库语言的解释和 编译两种实现方式

2、试着单纯讨论代数优化的局限性

3、使用C语言编写一个选择操作的规则优化程序

4、使用C语言编写一个连接操作的规则优化程序


第九章、事务管理

9.1、恢复机制概述

恢复引论:

数据对一个单位是非常重要的。也就是说,数据库的失效常常导致一个单位的瘫痪。

当数据库出现故障我们如何处理?

其实重点就两个字:防、治

数据库出现故障的时候,如果是防,我们就尽可能提高系统的可靠性;如果防不住,我们就治,在数据库系统发生故障后,把数据库恢复到之前的状态。一般来说,我们做第一手的准备不足以保证数据库数据的安全,比如突然停电,这种东西谁都防不住,所以通常在第一手准备的情况下,我们会去做第二手准备,而第二手准备,就是我们讨论的重点:恢复技术。

所以总结来看,我们设计的恢复机制一定有如下的东西:

一、数据冗余

硬件和软件出现故障,就会导致运行出现差错,也就会导致数据库失效。故障是前因,差错是现象,失效是后果。为了恢复失效后果,我们必须留有后备的副本以备不时之需,这样来看的话,备份就会导致数据冗余是必需的。

这里的冗余和数据库设计的冗余不一样,那里的冗余指的是数据库结构为了追求三范式,而有些地方不合理造成的冗余;而我们上面说的冗余是留有数据做备份。

二、要能够检测出故障,不能出现故障了还没有停下来,稀里糊涂的继续操作下去。

恢复技术有哪些?

有三类

单纯以后备副本为基础的恢复技术(周期性转储)

这个是从文件系统继承过来的恢复技术。这里要继续讲下去又得讲一下磁带了。

磁带就相当于小时候的录音带,你甚至可以理解为他是读卡器,他是一种可以脱机的存储设备,但是容易损坏。

所以使用以后备副本为基础的恢复技术时,我们会把磁盘上的数据库周期性转储到磁带上,当然你也可以刻在光盘上。由于磁带可以脱机存放,可以不受系统故障的影响,所以存在磁带的那部分数据库副本我们叫后备副本

那你可以试想,数据库如此庞大,假如真的让你那个类似于U盘一样的东西去拷贝,拷贝一次需要多久?而且在拷贝过程中数据库不能改变,相当于冻结了数据库,暂停其运作;所以在取后备副本的时候由于时间问题,我们不能频繁的取,一般周末夜间数据库空闲的时刻才会去操作。

但是这样一来你就会发现一个问题:假如我取后备副本的周期过长,那就会导致假如在上一次取后备副本到数据库突然故障这段期间我做了大量的更新的话,那么就会导致这些操作全部白干。

所以,我们想到了提高这个恢复技术的办法,也就是增量存储。这实际上是周期性转储方法的变种。一般来说,数据库不会改动整个结构,只会改动其中一小部分,我们可以把那一小部分所对应的物理块拿出来高频(也就是周期很短)地转储,其他的物理块可以很久才转储一次,这样的话,一旦发生故障,马上就能恢复最近更新的那一部分的物理块,损失度大幅下降。

由于这种方法实现起来很简单,所以在文件系统使用得很多,但是在数据库系统中只用于小型的和不重要的数据库系统。

以后备副本和运行记录(log或者journal)为基础的恢复技术

这种技术是企业里面用的最多的技术。

在SQLserver中,我们能看到建数据库的时候我们还会建一个日志文件log,这里就派生用场了。日志实际上就是一个流水账,我们也叫他是一个运行记录,记录你所有操作的流水;其中运行记录里面记了两样东西,一种是前像,一种是后像

前像(也就是老的数据,老值)(Before image,简称BI):

如果数据库被事务做了一次更新,那么我们就可以还没更新前数据库的数据涉及的物理块来恢复更新前的状态,也就是撤销更新,这种操作我们在恢复技术里叫做撤销(英文简称undo

后像(也就是新的数据,新值)(After image,简称AI):

同样的,我们更新后也有更新后数据涉及的物理块,如果更新的数据突然没了,我们可以根据这个物理块把更新的数据恢复到更新后的状态,也就是更新了然后我再做一次更新,也就是重做(英文简称redo

就拿修改表数据的相关操作来说,日志记录以下东西:

事务状态:

记录每个事务的状态,方便在恢复的时候做不同的处理。每个事务在提交的时候面临两种结局:

第一种是提交了才结束,这标志着事务成功地执行了,只有在事务提交后,事务对数据的影响才能被其他事务访问。

第二种是由于事务本身或者外部的原因,事务失败,也就是说这时候可能事务只做了一半,要继续做的时候事务出问题终止了,这时候为了消除那做了一半的影响,我们会对其做出卷回

如果是数据库失效了,可以取出后备副本,然后根据运行的记录,如果没提交就将前像卷回,这叫向后恢复。如果事务已经被提交了,但是提交前和提交后中间添加的数据没了,那我们必要时用后像重做,这叫向前恢复

基于多副本的恢复技术


9.2、事务和日志

事务英文名:Transaction

什么是事务?

一个事务是一个完整的业务逻辑单元,不可再分。

比如:银行账户转账,从A账户向B账户转账10000,需要执行两条Update语句。

update t_act set balance  = balance -10000 where actno = 'act_001';

update t_act set balance = balance + 10000 where actno = 'act_002';

以上这个过程是一起的,如果不一起,就会导致转账这件事失败。也就是说,以上两条DML语句必须同时成功,或者同时失败,不允许出现一条成功,一条失败。

要想保证以上的两条DML语句同时成功或者同时失败,那么就需要使用数据库的“事务机制”。

和事务相关的语句只有:DML语句。(insert delete update)

为什么?因为它们这三个语句都是和数据库表当中的“数据”相关的。事务的存在是为了保证数据的完整性,安全性

假设所有的事务都能使用一条DML语句搞定,还需要事务机制吗?

不需要事务

但实际情况不是这样的,通常一个事务需要多条DML语句共同联合完成。

事务可以保证多个操作原子性,要么全成功,要么全失败。对于数据库来说事务保证批量的DML要么全成功,要么全失败。事务具有四个特征:ACID

  • A原子性:整个事务中的所有操作,必须作为一个单元全部完成(或者全部取消),即:要么不做,要么全做。
  • C一致性:在事务开始之前和结束之后,数据库都保持一致状态
  • I隔离性:一个事务不会影响其他事务的运行

像window如果你太久不关机,就会导致出现一些怪事,比如你搞个死循环,那你的window系统很容易崩溃,但是如果你在linux里面

  • D持久性:持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束。也就是说,一个成功执行的事务对数据库的影响是持久的,即使数据库因故障而受到破坏,DBMS也应该能够恢复。

事务案例:

说明:

假如现在做一个银行转账系统,A给B转了s元,如果A余额不足,那么就会提示:insufficient fund(余额不足),然后把A余额-s元的这个操作回滚。

如果余额足够,那么A-s,B+s,然后提示转账成功,然后事务结束,利用Commit提交事务。

日志是什么?

日志一般存储了操作的流水,这里我们前面已经讲过了;而日志他是存储在非挥发存储器(有些书也叫非易失存储器)上。

所谓的非挥发存储器,指的是不会因为断电啊这样的操作而损失数据的存储器,而内存属于挥发性存储器,如果万一遇到停电,内存就没了。

运行记录的结构

活动事务表(Active Transacion list,简称ATL)

活动事务表记录所有正在执行,尚未提交的事务的标识符

正在执行、尚未提交标识符(Transaction Identifier,TID)。

提交事务表(Committed Transaction list,简称CTL)

这个表记录所有已提交的事务的标识符。

实际上这个表是这么运作的,当你把一个事务要提交了,那活动事务表会把TID交给提交事务表,然后在活动事务表中把该TID删除。但是,这两者顺序是不可颠倒的,如果颠倒,万一停电,就会导致TID刚在活动事务表中删除,然后在添加进提交事务表的那一刻由于故障没有任何记录。

日志(log)

日志里面分为三块

TID,前像文件和后像文件。

其中前像文件中的BID记录的是物理块的地址,空白部分记录的是正在更新事务的老值,这一个矩形就像但相当于一个物理块,前像文件就相当于一个堆文件,有很多物理块堆叠而成,如果一个关系需要卷回,就可以从前像文件中找出该事务的所有前像块,然后把它们全部写入由于故障被破坏的关系的对应块中从而消除该事务对数据库所产生的影响。

后像文件也和前像文件的结构一样

在每个日志中都应该记录前像文件和后像文件,以方便供以恢复使用。

提交规则和先记后写规则

一、提交规则:

后像(新值)必须在事务提交前写入非挥发存储器中,不能放在内存中,因为内存会消失。

写入非挥发存储器,也就是说,可以放数据库也可以放日志(前面说过日志也是一个非挥发存储器);万一这时候故障,即使没有放数据库,我们也可以利用日志中的后像文件中的后像重做;如果这时候有其他事务要访问这些数据,由于更新的内容已经保存在后像文件里面了,也可能在缓冲区中(因为后像文件是由多个物理块组成的),所以其他事务仍然可以去缓冲区或者后像文件找哪些更新后的内容。

二、先记后写规则:

如果后像在事务提交前写入了数据库,那必须把前像首先记入运行记录。

这句话可能有点抽象,也就是说假如我现在在做笔记,如果我没有保存以前写的旧笔记,然后直接把旧笔记删了,然后直接写新笔记,一旦突然断电,那就有可能新笔记和老笔记都没了。先记后写规则就是为了防备这一点。


9.3、更新策略以及故障后的恢复

更新操作:

undo和redo

undo(撤销)操作和redo(重做)操作具有幂等性。

举个例子:假如在编程中,我们设x = 10,后面又写了一条x = 20,那么就能把x = 10覆盖掉,其中后像20,前像10,但是后像20覆盖前像10的时候,不管覆盖多少次都是20,这就是幂等性,即做多次操作和一次操作的效果是等价的。

更新策略:

一、后像在事务提交前已经完全写入数据库(没人采用)

1、TID→ATL

2、BI→log

3、AI→DB,log

4、TID→CTL

5、从ATL删除TID

在每个数据库系统里总会有这么个模块——重启动模块,不同数据库系统叫法可能不同;他的作用是在重启动数据库系统后,检查上一次退出是否正常退出。

检查如上所说的更新策略时会出现三种情况

说明:

如果ATL里有而CTL没有,说明在提交阶段出问题了,那么系统会自动去undo收回那些事务。

如果ATL有,CTL也有,说明事务已经提交,但是ATL删除TID的时候出问题了,那么系统就会去删除TID

如果ATL没有,CTL有,说明过程执行没有出现差错,事务提交完毕。

二、后像在事务提交后才写入数据库(目前主流)

1、TID→ATL

2、AI→log

3、TID→CTL

4、AI→DB

5、从ATL删除TID

说明:TID提交ATL说明执行为提交,这时候由于后像没有在事务提交前写入数据库,所以不必记录前像,这时把后像记到日志,然后TID提交给CTL,这时候确定后像提交了,然后再把AI给DB,然后在ATL删除TID。

检查如上所说的更新策略会出现三种情况:

说明:

如果检查时发现ATL有TID而CTL没有,说明提交CTL过程出错,这时候由于还未在其他地方做出任何操作,无须undo,只需在ATL删除TID即可。

如果检查ATL和CTL都有TID,那么说明ATL删除TID出错,甚至AI搬到DB的阶段搬到一般故障了也有可能,所以这时候需要重新开始搬,即redo,然后在ATL里删除TID。由于幂等性,即使你前面搬到一半,现在redo重新搬也不会搬多。

如果ATL没有而CTL有说明过程结束了,无须任何操作。

三、后像在事务提交前后写入数据库(现在过时了)

策略思想:在第二个策略的基础上,人们想了一个办法,同样的不将后像在事务提交前急忙写入数据库,而是创建一个后台进程,当数据库空闲的时候后台进程将其唤醒,开始把后像搬到数据库,一旦数据库忙起来了,就暂停搬运。

1、TID→ATL

2、AI,BI→log

3、AI→DB(部分写入)

4、TID→CTL

5、AI→DB(继续完成)

6、从ATL删除TID

检查如上所说的更新策略会出现三种情况:

说明:

如果ATL有TID而CTL没有,说明BI可能有一部分在搬到数据库了,这时候需要做undo。

如果ATL有,CTL也有,说明AI可能搬到一半,ATL还没删除TID,所以为了继续搬完,我们要根据前像做redo,搬完后在ATL删除TID。

如果ATL和CTL都有,说明过程全部执行完毕,无须操作。


9.4、易地更新恢复技术(研究生)

9.5、并发控制概述

数据库中的并发概念

如果事务顺序执行,即一个事务完全结束后,另一个事务才开始,则叫这种执行方式为串行访问

如果DBMS可以同时接纳多个事务,事务在时间上重叠执行,则称这种执行方式为并发访问

在单CPU系统中同一时间一个事务只能占用一个CPU,那么多事务只能交叉使用一个CPU,这种并发叫做交叉并发

在多CPU系统中,允许多个事务同时占有CPU,这种并发方式叫做同时并发

并发的好处

1、改善系统的资源利用率和吞吐率(即单位时间内处理的事务数)

2、改善短事务的相应时间。

并发引起的问题

1、丢失更新

啥意思?假如现在有两个同学,A同学对一个变量a赋值10,B同学马上赋值20,结果A提交上去发现变量a的值莫名其妙变成了20。以上所说的简单的案例在数据库原理我们称为-写冲突

2、读脏数据

首先看图

这幅图反映了一个情况,有两个用户,一个用户T1在改动某条或者全部元组,一个用户T2读了两条元组,这就会导致什么问题呢,假如这个表是工资表,我是T2,我本来要算总工资,结果还没发工资前我在算总工资,算着算着T1那边把工资发下来了,然后我又算总工资,结果x的工资和y的工资是两个不同时期的工资,算出来的总工资肯定是不对的。我们把上述的情况称作-写冲突

实际上,上述情况还算是好的,假如有多个用户,如下图

T1改了x数据,实际上他还没更新完,只是更新了一次x,T2就急急忙忙拿到了错的x数据,这时候做出来的表达式运算后的错误结果y又被T3急急忙忙拿去用了,造成了一种错误的多米诺骨牌现象。

3、读值不可复现

举个例子,假如现在同学A他去查看银行的余额有100块钱,与此同时同学B把同学A的余额转账到自己的卡号上,结果同学A刚离开银行发现不对劲返回去查看余额发现余额变了。上面说的这个例子实际上也是一种读-写冲突。

这实际上也违背了事务的隔离性。

由此可知,实际上读是不会出问题的,问题出现在写上,如果同一时间段上有人改动数据就会引出一系列问题,并发控制的任务就是要避免访问冲突所引起的数据的不一致。解决这些问题传统方法是加锁,还有其他一些方法。


9.6、并发控制的正确性准则

调度概念:

在数据库系统中常常有多个事务并发运行,而一个事务内又有多种有序的操作,比如说一个事务先修改数据然后选择数据。这些操作由系统去安排其执行的顺序,安排的原则是:你既要交叉执行(你做完然后轮到我做然后你继续做),以充分利用系统资源;又要避免访问冲突。

我们设某一时刻并发执行的事务集合为S = {T1,T2,…,Tn},那么调度S就是对n个事务的所有操作的顺序做一个安排。比如说:

那么写成调度就是S = R2(t[x])W1(t)R2(t[x])

其中下标是事务的事务号。

可串行化

对于同一事务集合可以有多种调度,但是如果不同调度的结果一样,那么我们就说两个调度是等价的,也叫目标等价。如果并发控制后的调度和串行控制的调度是目标等价的,那么我们就称此调度为可串行化的(目标可串行化)。

因此,在一般地DBMS中,都是以可串行化作为并发控制的正确性准则。

冲突等价

我们前面提到了目标等价,实际上还有一种等价叫做冲突等价,简而言之就是,操作会发生冲突,如果发生冲突就会导致不打架,不冲突就等价。

比如我们前面提到的读脏数据,我们前面读了一个新值一个旧值,这就是冲突操作,冲突操作分为:读写冲突和读读冲突,前面都细讲了,现在就不细说了。

当然不冲突操作也有两种情况:

第一种是读同一个对象,这种情况下互换顺序也无所谓。

第二种是写不同对象,这种情况下互换顺序也无所谓。

这里我们可以知道,一旦把不冲突操作调换,就会产生一种新的调度,我们称为S的冲突等价调度。这样的话他们两个操作可以互换,不影响结果,所以一定目标等价。但是反之未必正确:即目标等价就一定是冲突等价。


9.7、封锁法以及锁协议

封锁法核心思想:

用加锁的方法实现并发控制,也就是在操作前先对操作现象进行加锁。

比如现在还是那个例子,同学A和同学B同时改变量x,那么我们就对变量x进行加锁,加锁后的x不允许多事务同时操作他,只允许先到事务去操作,比如先抢到改变x值机会的是A同学,那么A同学就会在做完改变x值后又提交会系统后,锁释放掉,B同学才能去操作变量x。

X锁

在这种加锁协议中,只有一种锁是X锁,X锁是一种排他性的锁,一旦有一个A事务在操作某对象时给对象加锁,其他事务就不得在A事务使用期间访问对象。

X锁中的X取自于排他性的英文(exclusive)

其相容矩阵如下:

两段封锁协议

定义一:

在一个事务中,如果加锁动作都在所有释放锁动作之前,那么我们称事务为两段事务(2PL)。上述的加锁限制称为两段封锁协议。

定义二:

一个事务如果遵守“先加锁,后操作”的原则,则此事务称为合式(well formed)事务。

定理一:

如果所有事务都是合式、两段事务,则他们的任何调度都是可串行化的。

总结:

1、合式+两段封锁协议:可串行化

2、合式+两段封锁协议+更新操作的锁在EOT(事务快要提交的时候)时释放:可串行化+可恢复的。

3、合式+两段封锁协议+所有操作的锁都在EOT时释放:严格的两段加锁协议(Strict 2PL protocol)

说明:严格的2PL协议简单可靠,应用很广,但是所有锁都要保持到EOT,不利于提高系统的并发度,尤其当一个事务的持续时间很长,或一个事务因等待某个资源(数据对象)而被阻塞时,它已获得的锁长期得不到释放。

(S,X)锁

在这种锁协议中,设有两种锁

S锁用于读,X锁用于写;S锁用于读则导致了多个用户可以并发读,因为我们前面知道,“读-写冲突”和“写-写冲突”根本原因都是写的问题,多个用户并发读不会出现所谓的“读读冲突”,所以S锁可以允许多个用户同时读,即多个事务同时申请S锁;但是,如果申请S锁就无法申请X锁,这是由于X锁是排它锁,不兼容其他锁,假如事务A申请了S锁然后做读操作,事务B申请了X锁做写操作,那就会导致出现“读写冲突”。

其相容矩阵如下:

(S,U,X)锁

在这种锁协议中,在前两个协议的基础上又加了一种U锁。

我们知道,事务在做更新操作前,一般先读出老的内容,然后在内存修改后,在往数据库写入修改后的内容。这里我们有两个过程,一是内存修改,二是数据库写入,U锁就相当于在第一阶段作为S锁存在,第二阶段作为X锁存在。

这样的话,不用全程X锁把数据全锁住,进一步提高了并发度。

总结:

从执行效率来看,SUX锁效率最高;但是从系统复杂度和实现难度来看,SUX锁也是最高的。


9.8、死锁和活锁

如果我们采用(S,X)锁,那就会出现一个问题,如果一个数据对象被事务A申请了一个S锁,那么事务B也可以申请S锁,那么这个时候事务A是无法申请X锁的,假设这个时候有n个事务都申请S锁,那么事务A的X锁就会迟迟无法申请,这个锁就叫活锁

活锁:(扼死现象)

若某对象加了S锁,这时候有其他事务申请对他的X锁,则需等待;若某对象加了S锁,这时候若有其他事务申请了对他的S锁,按相容矩阵,无须等待,而X锁的申请迟迟不能获得批准,这种现象叫活锁。示意图如下:

如何避免活锁:

避免活锁的简单方法是采用先来先服务的策略。

X锁假如是第二个来的,那么第三个来的S锁就不能继续申请,得让X锁先来;这样一来,比X锁后申请的S锁就不会先于X锁获准了。

死锁:

一个事务如果申请锁而没有被批准,则需等待其他事务释放锁,这就形成了事务之间的等待关系,如果有两个数据对象D1,D2,T1先申请到D1,T2先申请到D2,然后后面T1又想申请D2,此时D2被T2抢先,所以T1需要等待;而这个时候T2想申请D1,无独有偶,D1被T1抢先,所以T2也在等,这样两个人都在等对方,两个人都等不到,形成了死锁。示意图如下:


9.9、死锁检测和死锁预防

对付死锁就和恢复机制是一个道理,先防,防不住再治。

其中防就是防止死锁,治就是检测死锁,发现了然后处理。

死锁检测

一、超时法

如果一个事务的等待时间过久,我们有理由怀疑他里面死锁了,这个理由虽然简单,但是容易误判,比如事务因为其他原因(系统负荷过大,通信受阻)而使事务超过时限,也可能被判定为死锁。当然死锁的时限也不能设太大,避免发现死锁的滞后时间过长。通常时限须根据系统运行情况通过试验确定。

通常大型数据库系统不会采用这种方法,而采用下面的等待图法。

二、等待图法

等待图是一个有向图G = (W,U)。其中,W是结点的集合,W = {Ti|Ti是数据库中运行的事务,i = 1,2,…,n},U是边的集合。DBMS根据锁申请和加锁的情况,动态地维护一个等待图。当且仅当等待图中出现回路时,死锁才发生。

当运行的事务比较多,我们肯定不可能去看每个边会不会构成环,这样维护等待图和检测回路的开销比较大,影响系统的性能。比较合理的办法是周期性地进行死锁的检测。

解决死锁

一旦发现死锁,DBMS就要做以下措施;可以在回路中挑选一个年轻的事务作为牺牲者,把它后台杀掉,然后给其他事务让路,这样其他事务就能动起来。

牺牲的事务要做两步处理:

一是发消息给相关用户,告知他们事务已经因为死锁而被撤销,也就是事务已经不再是三条线造成的死循环关系了,通知系统重新安排改成一条线

二是由DBMS重新启动该事务,不过这一个事情要慢点,不能说刚杀完后台就马上重新启动,这样可能又出现死锁。

年轻的事务符合下面三个条件:

最迟交付的

获得锁最少的

卷回代价最小的

死锁防止

检测死锁需要一定的开销,所以如果能防止死锁发生,不就不用检测了吗?

操作系统中也曾经提到过死锁防止,比如在事务执行前,直接就把需要的锁全申请了,那么这个事务他要么去等待别人,要么别人来等他。

事务重执策略(这部分还没看懂,看懂了再补笔记)

1、等待-死亡策略

2、击伤-死亡策略


9.10、多粒度封锁(研究生) 

前面我们将封锁,只是说对一个数据对象加锁,并没有说这个数据对象有多大。

但是实际上,这个数据对象可以是数据库、一个区域、一个文件或者一个关系,甚至是一条元组或者某个属性,封锁单位越大越粗,封锁起来越简单;封锁单位越小越细,往往需要加很多锁,锁起来更麻烦。

好比一个屋子,你怕进小偷,所以你直接把大门锁了,那肯定方便,一个锁就能搞定;但是如果你不锁大门,那你就得把房间门全锁了,这就比较麻烦了。

但是直接锁大门,可能很多细节把握不了,现比如客人来参观大厅,小偷偷房间的贵重;如果你的来访者有些是客人有些是小偷,你大门一关谁都进不来,但是你只关放贵重物品的房间,就能防小偷,不用防客人。

所以我们如果直接大单位加锁锁死,就会导致把一些不需要加锁的数据也封锁了,降低了并发度。

对于上述的情况,我们可以发现,生活中实际上有些表要访问全部,有些访问某些属性即可,所以,我们通常提供多级封锁单位,根据应用的需要来启用,比如我提供了数据库锁和表锁,你需要哪个就用哪个。这种封锁方法我们叫做多粒度封锁

在现代大型数据库系统一般都支持多粒度封锁。但是在微机DBMS中,并发度要求不高,一般以关系作为封锁单位,这称为单粒度封锁

采用多粒度封锁时,一个对象可能以两种方式被锁住了。

举个例子,假如你的数据对象是房间,那房子大门被锁了是不是相当于房间也进不去了,这就是隐式封锁。直接锁房间门就是显式封锁

我们封锁的时候应该尽可能控制封锁范围,以免影响其他事务的运行,降低系统的并发度。

锁的冲突问题:

如果只有显示锁,那我们完全可以像前面学过的那样去检测锁的冲突或者防止死锁等等,维护起来也比较方便;但是如果有了隐式锁,那检查锁的冲突就比较复杂了。

假如你现在在检查房间锁,这时候假如你已经进入了屋子,那么说当你检查完一圈系统还是说你没有检查完所有的锁,这时候回去大门,诶,发现大门被锁上了,是不是很难受。有时候你好不容易检查完房间锁,结果发现还是没有检查完,然后看了一下大门大门没锁,结果看了一下是房间里的保险箱上锁了,是不是更难受?

所以,为了简化这种检查,IBM公司开发了所谓的意向锁(intent locks)。

意向锁:

IS锁:

他被叫做意向共享锁。共享,实际上就是读操作;所以当一个数据对象被加了IS锁,表示它的某些子孙加了或者拟加了S锁(拟加,也就是说,如果你不单独设置子对象的锁,那么他直接给你上S锁,如果你单独设置了子对象的锁,那就以你设置的为准)。

如图所示,如果元组加了S锁,现在如果有个其他事务拿不到元组的锁权,那么它就会去想拿元组所在的关系甚至是数据库的锁权,这时候如果其他事务成功上锁,那么对元组加了S锁的事务将无法访问。

所以为了应对这种情况,我们可以在申请元组的S锁权时用IS锁同时锁住数据库和关系,防止其他事务搞事,导致隐式锁和S锁冲突。

IX锁:

他称为意向排他锁,同上面说的一样,为了防止其他事务搞事情,在对元组加X锁的时候要给关系和数据库同时加锁IX锁。

SIX锁:

SIX锁相当于加了S锁然后再加上IX锁,即SIX = S+IX。在实际应用中,常常需要读整个关系,并且更新其中个别元组。

范例:工资表,看所有人,然后改其中个别员工工资。

有些DBMS没有SIX锁,这时候对于这种想看整体,修改个别的访问,只有以下两种可供选择的加锁方案:

方案一:

在元组的父(关系)上加个X锁,关系的父(数据库)加个IX锁(防止其他事物动手),又由于X锁是排它锁,S锁不能存在。在关系加了X锁就相当于把关系中所有的元组上了X锁,但是这样的话其他事务就不能访问了一大堆东西(比如关系中的数据),降低了并发度。

方案二:

所有需要更新的元组加X锁,其他元组加S锁,关系和关系以上的各级加IX锁。IS锁只能读不能操作,所以我们才用IX锁代替IS锁,这样的话其他事务可以对元组进行访问了。

方案二看起来比方案一好,但是在面临大数据的情况下,假设有一万条元组,那么加的S锁和X锁就有一万个。所以SIX锁这时候就体现出优势了。

意向锁的相容矩阵:


9.11、索引并发控制(研究生)

9.12、幽灵及其防止(研究生)

9.13、事务隔离等级(研究生)

9.14、时间标记并发控制技术(研究生)

9.15、乐观并发控制技术(研究生)

9.16、作业

1、各种数据有不同的恢复要求;有些要求恢复至最近一致状态,有些只要求恢复至先前的某个一致状态;也有些数据不需要恢复措施,只须在失效后重建。试着各举一例。

2、试着论述提交规则和先机后写规则对事务的必要性

3、undo和redo是两种基本的恢复操作,试说明其操作内容及其所需的条件。在什么情况下需要redo,在什么情况下需要undo

4、讨论易地更新恢复技术的优缺点。在易地更新方案中,是否需要运行记录?在什么环境下可以省去运行记录?

5、论述各种失效情况下应该采取的恢复措施

6、试着回答下面的问题

什么叫并发?

为什么要并发运行?

并发会引起什么样的问题?

什么样的并发执行才是正确的?

如何避免并发所引起的问题

7、什么是两段封锁协议?怎么实现两段封锁协议

8、分别讨论写锁和读锁在事务结束前释放可能引起的问题。

9、什么叫活锁,如何防止活锁?

10、什么叫死锁,如何防止死锁?

11、试着区别串行调度和可串行化调度。请各举一例

12、试分析、比较封锁法和时间标记法两种控制并发技术

13、试分析、比较悲观法和乐观法两种并发控制技术。


第十章、安全性和完整性约束

10.1、概述

数据库是共享的资源,所以破坏一般都源自于共享。

破坏分类:

1、系统故障(断电断网等等)

解决:恢复技术

2、并发所引起的数据不一致(死锁活锁等封锁法问题)

解决:并发控制

3、人为的破坏,如数据被不该知道的人访问,甚至被篡改或破坏

数据库安全

4、输入或更新的数据库数据有误,更新事务未遵守保持数据库一致性的原则。

完整性约束

维护数据库安全的常见手段

视图定义和查询修改

假设现在有一个计算机学院的老师,我们不想给他看到所有学生所在基表(最原始的表),我们只想给他看到计算机学院的学生,那怎么办呢,我们可以定义一个视图,视图里面也是一张表格,但是只放了计算机学院的学生。

有些DBMS没有视图这种功能,那么他会提供一种叫查询修改的功能。查询修改,就是你的查询语句比如说还是计算机老师,你只需要给他看计算机学院学生的表,那你可以让DBMS对他写的查询语句做一个改写,比如老师写的语句是select* from student,那么DBMS会自动补上条件:select * from student where college = "计算机"。让这个老师只能查到数据库中计算机学院学生的部分,这就是查询修改。

访问控制

数据库的各种资源都是有一定的操作权利的,DBMS可以通过控制这些权利把用户划分开来,以保证数据库的安全。一般来说,我们会划分成三类:

1、一般数据库用户

在SQL中,这种用户称为“具有CONNECT特权的用户”,可以和数据库连接,并且拥有下列特权:

(1)可以按照授权查询或更新数据库的数据

(2)可以创建视图或定义数据库的别名

2、具有支配部分数据库资源特权的数据库用户

在SQL中,这种用户称为“具有RESOURCE的用户”,除了具有一般数据库用户的所有权利外,还有一下特权:

(1)可以创建表、索引和簇集

(2)可以授予或收回其他数据库用户对其所创建的数据对象所拥有的的访问权。

(3)有权对其所创建的数据对象跟踪审查

3、DBA数据库用户

DBA拥有支配整个数据库的权利,除了上面说的,他还可以:

(1)有权访问数据库中的任何数据。

(2)不但可以授予或收回数据库用户对数据对象的访问权,还可以批准或插销数据库用户

(3)可以为public定义别名,public是所有数据库用户的总称

(4)有权对数据库进行调整、重组或重构

(5)有权使用DBMS中自管理、性能调优等工具。

已经划分好了,那么用户怎么拥有对应的身份去访问数据库呢。一般访问分为以下两个步骤:

1、用户的标识和鉴别

(1)利用只有用户知道的信息鉴别身份

这种技术一般是用口令,也就是所谓的密码,不过密码实际上也有泄密的危险,所以DBA应该督促用户多换口令。

(2)利用只有用户具有的物品鉴别用户

就像钥匙一样,比如磁卡。

(3)利用用户的个人特征鉴别用户

指纹,虹膜等。

2、授权(Authorization)

授权就是给予用户一定的访问权限,这是对用户访问权限的规定和控制。

(1)授予某类数据库用户的特权

假如是皇上选妃,那么这个操作就是给个普通人一个名分,而且这个名分只有皇上(DBA)才能给。

(2)授予某些数据对象进行操作的特权,可以由DBA授予,也可以由该数据对象的创建者授予。

这种操作看着简单,实际上在企业级数据库非常难操作,因为企业级数据库的数据对象十分庞大,不可能一个一个都去设置特权;在这种情况下,我们提出了角色(role的概念。角色,实际上就是某一类数据对象,当我们提出授权的时候,直接对角色授权,可以大幅度提高授权管理的效率。

3、数据加密(Data encryption)

我们的数据库系统是建立在操作系统之上的,也就是说如果要访问数据库中的数据,必须经由DBMS进行访问,而不能绕过DBMS从操作系统进行访问,而这个控制,是由操作系统来负责保证的。而尽管有这么多措施,但是数据存储在介质上(如硬盘、磁盘),还常常通过通信线路进行传输。如果有人窃取磁盘或磁带,或从通信线路窃听,则数据库系统就无法控制了。所以当我们对数据的保密性特别高的时候,我们可以对数据进行加密,这样即使有非法分子绕过DBMS从操作系统拿到了你的数据,他也无法读出。

不过这么做常常要付出代价,由于加了密,所以在数据库中取出文件供自己使用时,常常需要解密,解密的过程有时候很繁琐,会影响一些效率。

4、跟踪审查(Audit trail)

即使你进行数据加密,但还是有牛马人能突破这些限制来窃取你的数据。 这时候既然防不住也治不了,干脆监视,一旦发现有人窃取了,就打开我们在数据对象上安置的审计,然后看下是谁做了什么操作动了什么手脚。这个审计我们一般有术语叫做跟踪审查记录。他一般包含下列的东西:

1、操作类型

2、操作终端标识和操作者标识

3、操作日期和数据

4、所涉及的数据

5、数据的前像和后像


10.2、统计数据库的安全

10.3、完整性约束

数据库里关系的一个合法实例必须满足的条件我们叫做完整性约束。

完整性约束分为一下两个:

静态约束

固有约束(static constraints)

指数据模型固有的约束,如关系的属性应该是原子性的,不允许表中套表,也就是我们所说的应该满足一范式的限制。

隐含约束(implicit constraints)

指隐含于数据模式中的约束,一般用DDL语句说明,并且存在于数据目录中。例如,域完整性约束、实体完整性约束以及引用完整性(外键约束等等)约束,他们都用相应的DDL语句说明。

显示约束(explicit constraints)

固有约束、隐含约束是最基本的约束,但概括不了所有的约束。数据完整性约束是多种多样的,并且依赖于数据的语义和应用,这些约束只有显式地说明,所以叫显式约束

动态约束

完整性约束的说明

用过程说明约束

根据外键,用这个关系1(表)里的某个键,可以引用(寻找)其他关系(表)中的一条元组,即可以引用其他表的主键,那我们叫这个其他表里的主键键叫外键。

现在有两个表r1和r2,k1是r1的主键,k1是α的外键,那么我们要保证下列条件

α在r2表示的投影要包含于k1在r1表上的投影

如果你要在r1做插入操作,那么怎么插入都没事,因为是r2来引用r1,r2只需要在r1里面找得到对应的元组即可,所以r1怎么插入都没事,只需要保证r2的α所在的值是K1的值的子集即可,但是你如果在r2进行操作,那你一定要去检查k1是否有这个新插入的α值,如果没有,则不允许插入,因为在引用的时候他去k1找不到对应的引用值。

如果你在r2做删除操作,那你怎么删除都没事,但是如果你想删r1,那你要注意删r1的时候要保证r1的k1值无α引用,如果有引用,那么用户可以在定义外键的时候选择这两种情况:

1、系统报错,不让你删

2、你强制要删,那么先把r2中被k1引用的相关α删除,然后再删k1,我们把这个操作叫做级联删除

如果你在r2做更新操作,那就类似于刚才的插入操作,更新时要注意r2中的α是否是k1的子集,否则引用时就会出现外键当空;如果你在r1做更新操作,那就类似于刚才的删除操作,更新时要注意r1的k1删除后是否会影响α的引用,避免出现外键当空。

在做这些操作时,如果违反约束,那么DBMS会卷回事务。

用断言说明约束

断言指的是数据库状态必须满足的逻辑条件,你可以理解为if语句。

数据库完整性约束可以看成一系列断言的集合。为了表示这个断言约束,自然就有相关的断言说明语句,用这个语言我们可以写出数据库的完整性约束,由系统编译成约束库。DBMS中还有一个完整性控制子系统,对每个更新事务,用约束库中的断言对其进行检查,如果发现更新违反约束,就卷回事务。

范例:assert 余额约束 on 储蓄账:余额>=0;

这种方法有优点也有缺点,优点是免除了程序员在应用程序中分散地考虑完整性约束问题。这既减少了编程的麻烦,又方便了应用程序和约束的维护。

但是,完整性控制子系统实现起来非常复杂,开销也很大,降低了数据库更新操作的性能。

在基表定义中加CHECK子句约束

定义基表的时候添加CHECK,也就是creat表的时候添加CHECK,想要了解可以前往CHECK。

范例:

create table Sailors

(

sid integer,

sname char(10),

rating integer,

age real,

primary key (sid),

check (rating >=1 and rating <=10)

//利用check来控制rating属性的域。

)

单个表用CHECK,多个表用断言。

用触发子表示约束

触发子,也叫触发器(Trigger subsystem)

后面会来论述。

完整性约束的实施

因为完整性约束是伴随数据库更新操作进行的,对数据库的更新操作性能影响很大。

目前域完整性约束在一般的DBMS中都已经实施;

实体完整性约束在大部分关系DBMS中都已经基本实施;

应用完整性约束在部分关系DBMS中已经实施;

显式完整性约束在商品化的DBMS中实施的也逐步增多。

随着计算机性能的提高和约束检验方法的改善,在DBMS商品中,比较全面的完整性约束检验可望成为标准功能。


 第十一章、触发子和主动数据库系统

11.1、引论(研究生)

一、简介

传统数据库过于被动,需要DBA去操作才能动起来,导致无法应对一些特殊问题,这样的数据库是被动的;而后来由于对主动数据库的要求日益增加,主动数据库系统就称为公认的数据库重要研究方向之一。

二、主动数据库

主动数据库仅仅是一个数据库系统的功能罢了,并不是某一类数据库。也就是说,主动数据库就是具有主动数据库功能的数据库系统。

三、触发子系统

在早期的关系数据库管理系统System R中,就有人建议增加触发子系统。

触发子系统在满足一定的条件时,就会触发一定的动作。


11.2、数据库的更新与引用完整性(研究生)

11.3、触发器(研究生)

11.4、触发器的应用(研究生)

第十二章、数据库设计

12.1、数据依赖

要设计一个好的数据库,你要充分理解用户的需求。

实际上,我们实际应用中的这些数据都不是孤立存在的,他们都存在各种各样相互的内在的联系,我们把这种联系统称为叫数据依赖关系。

即:Some dependent relations exist between attributes.

那数据依赖关系有哪几类呢?

函数依赖(Functional Dependncy,FD)

其中依赖关系里最基本的一种我们叫做函数依赖。由于函数是一个多对一映射,所以我们可以引出他的概念。

函数依赖就是某个值或者某个属性组能够决定其他一个属性的值。

而这种关系恰恰是数据库设计里最重要的。

多值依赖(Multi-valued Dependency,MVD)

他的定义是一些属性的值可以决定另外一些属性的值。

也就是说,函数依赖是多值依赖的一个特例。

但是实际上现实应用多值依赖是很少的。

连接依赖(Join Dependency,JD)

假如S是供应商编号,P是零件编号,J是工程编号

如果一个表能够通过分解,分解成三张表,然后这三张表又能通过连接拼回原来的表,一条元组不落下,那我们就说这个表中的三个属性之间存在连接依赖,我们把这种拆分后拼接回去不落下元组的拼接叫无损拼接

但是实际上现实应用连接依赖是很少的。


11.2、关系模式的规范化

第一范式(1NF)

在传统的关系数据模型中,曾经限制关系的属性必须是原子的。这个限制条件被命名为第一范式。用俗话来说就是不允许表中套表。

如上图,第一张表就是突破了一范式的限制,这样的表用Creat的SQL语句是建不出来的,所以必须要把子表给“拍平”

如果你不想拍平,你也可以再做一个张表,把一张表的属性拆成两张表。

第二范式(2NF)

在关系满足一范式的前提下,没有属性对主键产生部分函数依赖,那我们就说这个关系满足二范式。

我们可以发现,虽然主键是(S#,C#),但是sname不靠主键,仅靠主键的S#即可确定,所以我们说其不满足二范式。

当然,这张表在数据库中可以建立,但是会出现很多问题。

违背第二范式所产生的问题:

插入异常:

假如以上面的例子为例,我们现在有个新生刚来学校,但是他还没选课,导致C#为空,前面我们讲主键约束时曾经讲过主键不能为空,这就导致了包含了这个新生的基本信息的这条元组插不进表。

删除异常:

如果一个学生不小心选错了课,那么如果做退课操作,他的所有信息就会丢失,因为主键不能为空嘛。

更新艰难:

以上面例子为例,最后有个属性是grade,说明由于分数问题,一个人的其余基本信息也要照抄,假如每门课分数不一样,同个人就要重复写五十次,存在大量数据的冗余。这就是你表设计的结构不合理。

如何解决违背二范式所产生的问题?

根据“一事一地”的规则去设计你的表结构,一张表只管一件事。

因为我们可以看到上面的表出问题了就是因为我们一个表管了两件事。

第三范式(3NF)

在第二范式的基础上,不存在对主键的传递依赖即为三范式。

也就是说,工资是由职工所在的工资等级决定的,而工资等级又是由员工编号决定的,这是一个传递依赖。

违背第二范式所产生的问题:

插入异常:

现在拿上面的例子举例,如果有一批新员工进来了,虽然工资等级和工资数有参照,但是老板还没有分配那个员工对应那个工资等级,这时候这个表就没法录入数据。

删除异常:

现在拿上面的例子举例,如果只有一个人拿三级工资,那么当这个人跳槽跑掉了,那么三级工资对应的那个工资等级也会被删掉。但是实际中,我想要的理想状态是即使这个员工跑掉了,但是三级工资等级对应的工资这个对照关系我还是要留着。

修改艰难:

同样的,工资和员工编号有很多不一样的,但是工资等级有很多一样的,这样就会导致每个表都有SAL_LEVEL这个属性,而且每个都要写对,写错了就会导致数据不一致。

解决方法:

一事一地。

一般只需要达到3NF的表就很合理了。

第四范式(4NF)

 消除了多值依赖

第五范式

消除了连接依赖

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/279734.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号