数据管理基础

记着记着就觉得没意思,全是概念化的东西,过于抽象了属于是,感觉大致有个了解也就行,不过复习过程中感觉讲课其实还是挺有脉络和条理的。

Part1——数据管理导论

数据

计算 = 算法 + 数据

数据是描述事物的符号化定义

数据的含义是数据的语义,数据与其语义不可分割

几种数据管理方式的对比:

方式数据管理者数据面向对象共享程度独立性结构化数据控制能力胜任
人工管理用户(程序员)单一某一程序无共享不独立,完全依赖于程序无结构应用程序自己控制“挥发性计算”
文件系统文件系统,可长期保存某一应用共享性差独立性差记录内有结构,整体无结构应用程序自己控制“持久性计算”
数据库管理系统数据库管理系统多个共享应用共享性较高独立性较高按一定数据模型组织由数据库管理系统控制“共享性计算”

数据库

数据库的定义:Database,是长期存储在计算机内,有组织可共享大量数据集合

数据是数据库中存储的基本对象

数据库管理系统

数据库管理系统(DBMS):位于用户和操作系统之间的一层数据管理软件,是基础软件,是一个大型复杂的软件系统

DBMS用途:科学地组织并且存储数据,高效获取和维护数据

DBMS 主要功能
  • 数据定义
    • 提供数据定义语言
    • 定义数据库中数据对象
  • 数据的组织/管理/存储
    • 分类组织、存储和管理各种数据
    • 确定组织数据的文件结构和存取方式
    • 实现数据之间的联系
    • 提供多种存取方法提高存取效率
  • 数据操纵功能
    • 提供数据操纵语言
    • 实现对数据库的基本操作 (查询、插入、删除和修改)
  • 数据库的事务管理和运行管理
    • 数据库在建立、运行和维护时由数据库管理系统统一管理和控制
    • 保证数据的安全性、完整性、多用户对数据的并发使用
    • 发生故障后的系统恢复
  • 数据库的建立和维护功能
    • 数据库初始数据的装载和转换
    • 数据库转储、恢复功能
    • 数据库的重组织
    • 性能监视、分析等
  • 其它功能
    • 数据库管理系统与网络中其它软件系统的通信
    • 数据库管理系统系统之间的数据转换
    • 异构数据库之间的互访和互操作

数据库系统

数据库系统(Database System,简称DBS),在计算机系统中引入数据库后的系统构成

数据库系统的构成:

  • 数据库
  • 数据库管理系统(及其应用开发工具)
  • 应用程序
  • 数据库管理员(DBA)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MPRQet1I-1687783562721)(null)]

数据库的特点:

  • 数据结构化
    • 整体结构化
    • 数据用数据模型描述,无需应用程序定义
  • 数据的共享性高,冗余度低且易扩充
    • 数据面向整个系统,可以被多个用户、多个应用共享使用
  • 数据独立性高
    • 物理独立性
    • 逻辑独立性
    • 数据独立性由数据库管理系统的二级映像功能来保证
  • 数据由数据管理系统统一管理和控制
    • 数据库管理系统提供数据控制功能
    • 数据库恢复(Recovery)

数据模型

数据模型是对现实世界数据特征的抽象,用以抽象、表示和处理现实世界中的数据和信息

数据模型应满足三方面要求:

  • 能比较真实地模拟现实世界
  • 容易为人所理解
  • 便于在计算机上实现

数据模型是数据库系统的核心和基础

概念模型,也称信息模型

  • 按用户的观点来对数据和信息建模,用于数据库设计

逻辑模型

  • 按计算机系统的观点对数据建模,用于DBMS实现,主要包括网状模型、层次模型、关系模型、面向对象数据模型、对象关系数据模型、半结构化数据模型等。

物理模型

  • 是对数据最底层的抽象,描述数据在系统内部的表示方式和存取方法

客观对象的抽象过程—两步抽象

  1. 现实世界中的客观对象抽象为概念模型
    • 将现实世界抽象为信息世界
  2. 把概念模型转换为特定DBMS支持的数据模型
    • 将信息世界转换为机器世界

数据模型组成要素

数据结构

数据模型的数据结构:

  • 描述数据库的组成对象,以及对象之间的联系

描述的内容:

  • 与对象的类型、内容、性质有关
  • 与数据之间联系有关

数据结构是对系统静态特征的描述

数据操作

数据模型的数据操作:

  • 对数据库中各种对象(型)的实例(值)允许执行的操作的集合,包括操作及有关的操作规则

数据操作类型:

  • 查询
  • 更新

数据操作是对系统动态特征的描述

数据的完整性约束条件

数据的完整性约束条件——一组完整性规则的集合

  • 完整性规则:给定的数据模型中数据及其联系所具有的制约和依存规则
  • 用以限定符合数据模型的数据库状态以及状态的变化,以保证数据的正确、有效和相容

数据模型对完整性约束条件的定义:

  • 反映和规定必须遵守的基本的通用的完整性约束条件。
  • 提供定义完整性约束条件的机制,以反映具体应用所涉及的数据必须遵守的特定的语义约束条件。

概念模型

概念模型

概念模型用途:

  • 概念模型用于信息世界的建模
  • 是现实世界到机器世界的一个中间层次
  • 是数据库设计的有力工具
  • 数据库设计人员和用户之间进行交流的语言

概念模型要求:

  • 较强语义表达能力
  • 简单、清晰、易于用户理解

基本概念

实体(Entity):

  • 客观存在并可相互区别的事物称为实体。
  • 可以是具体的人、事、物或抽象的概念。

最近书写软工二大作业“铁路购票系统”时,对于数据库才算是真正跟实际联系起来,之前一直觉得很虚。

属性(Attribute):

  • 实体所具有的某一特征称为属性。
  • 一个实体可以由若干个属性来刻画。

码(Key):

  • 唯一标识实体的属性集称为码

实体型:

  • 用实体名及其属性名集合来抽象和刻画同类实体称为实体型
  • 实体的模板

实体集:

  • 同一类型实体的集合

联系:

  • 现实世界中事物内部以及事物之间的联系在信息世界中反映为实体(型)内部的联系和实体(型)之间的联系。

实体之间的联系,指不同实体集之间的联系,有一对一,一对多,多对多等多种类型
实体内部的联系,指组成实体的各属性之间的联系

实体-联系方法

画E-R图重点

用E-R图来描述现实世界的概念模型,E-R方法也称为E-R模型

  • 实体
  • 属性
  • 联系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUDRqLEd-1687783560408)(null)]

上图较为全面展示了多种实体之间的联系,不过我不是很清楚为什么联系,如销售、聘用这些也给他们安上了属性,按理来说只有实体才会具有属性。网上诸多E-R图也并未如此。
虽然我们所学E-R图是极其简单的,但这里有篇E-R教程,其中也是进行了更加详细地介绍。

逻辑模型

如何“多快好省”地将信息世界转换为机器世界?

  • 基本问题,如何在机器世界中表达“低层”数据结构和“高层”数据结构?
  • 方案1:尽量独立于应用层,采用“中立”的方式表达概念模型
  • 方案2:在应用层中,使用特定数据结构,并在逻辑模型中高效支持这一数据结构
  • 方案*:通用数据结构采用方案1,关键性数据结构采用方案2

常用数据模型:

  • 格式化模型
    • 层次模型(Hierarchical Model)
    • 网状模型(Network Model)
  • 关系模型(Relational Model)
  • 对象模型
    • 面向对象数据模型(Object Oriented Data Model)
    • 对象关系数据模型(Object Relational Data Model)

回头复习才发现,原来主要讲述的内容早在最初几节课就已经说了,讲述的脉络也是按照关系型数据库、NoSQL数据库的顺序进行。温故而知新,如是而已。

关系模型

在用户观点下,关系模型中数据的逻辑结构是一张二维表,它由行和列组成。

一些概念:
关系(Relation):一个关系对应通常说的一张表
元组(Tuple):表中的一行即为一个元组
属性(Attribute):表中的一列即为一个属性,给每一个属性起一个名称即属性名
主码(Key):也称码键。表中的某个属性组,它可以唯一确定一个元组
域(Domain):是一组具有相同数据类型的值的集合。属性的取值范围来自某个域。
分量:元组中的一个属性值。
关系模式:

  • 对关系的描述
  • 基本格式:关系名(属性1,属性2,…,属性n)
  • 学生(学号,姓名,年龄,性别,系名,年级)

关系必须是规范化的,满足一定的规范条件
最基本的规范条件:关系的每一个分量必须是一个不可分的数据项, 不允许表中还有表

关系模型的操作与完整性约束:
数据操作(查询、插入、删除、更新)是集合操作,操作对象和操作结果都是关系
存取路径对用户隐蔽,用户只要指出“干什么”,不必详细说明“怎么干”
关系的完整性约束条件:

  • 实体完整性
  • 参照完整性
  • 用户定义的完整性

关系模型的优缺点:

  • 优点:
    • 建立在严格的数学概念的基础上
    • 概念单一
      • 实体和各类联系都用关系来表示
      • 对数据的检索结果也是关系
    • 关系模型的存取路径对用户透明
      • 具有更高的数据独立性,更好的安全保密性
      • 简化了程序员的工作和数据库开发建立的工作
  • 缺点:
    • 存取路径对用户透明,查询效率往往不如格式化数据模型
    • 为提高性能,必须对用户的查询请求进行优化,增加了开发DBMS的难度

NoSQL

  • 基于Key-Value存储模型
  • 基于文档模型
  • 列族模型
  • 基于图模型
    • 记为G (V, E),V为结点(node) 集合,每个结点具有若干属性,E为边(edge) 集合,也可以具有若干属性。

数据库系统的结构

概述

从数据库应用开发人员角度看:

  • 数据库系统通常采用三级模式结构,是数据库系统内部的系统结构

从数据库最终用户角度看,数据库系统的结构分为:

  • 单用户结构
  • 主从式结构
  • 分布式结构
  • 客户-服务器
  • 浏览器-应用服务器/数据库服务器多层结构等

一些概念

模式:数据库逻辑结构和特征的描述,是型的描述,不涉及具体值,反映的是数据的结构及其联系模式是相对稳定的

实例:反映数据库某一时刻的状态,模式的一个具体值,同一个模式可以有很多实例,实例随数据库中的数据的更新而变动

三级模式(重点)

模式
  • 模式(也称逻辑模式)
    • 数据库中全体数据的逻辑结构和特征的描述
    • 所有用户的公共数据视图
  • 一个数据库只有一个模式
  • 模式的地位:是数据库系统模式结构的中间层
    • 与数据的物理存储细节和硬件环境无关
    • 与具体的应用程序、开发工具及高级程序设计语言无关
  • 模式的定义
    • 数据的逻辑结构(数据项的名字、类型、取值范围等)
    • 数据之间的联系
    • 数据有关的安全性、完整性要求
外模式

也称子模式或用户模式
数据库用户(包括应用程序员和最终用户)使用的局部数据的逻辑结构和特征的描述
数据库用户的数据视图,是与某一应用有关的数据的逻辑表示
外模式的地位:介于模式与应用之间
模式与外模式的关系:一对多
外模式通常是模式的子集
一个数据库可以有多个外模式。反映了不同的用户的应用需求、看待数据的方式、对数据保密的要求
对模式中同一数据,在外模式中的结构、类型、长度、保密级别等都可以不同
外模式与应用的关系:一对多
同一外模式也可以为某一用户的多个应用系统所使用
但一个应用程序只能使用一个外模式
外模式的用途:

  • 保证数据库安全性的一个有力措施
  • 每个用户只能看见和访问所对应的外模式中的数据
内模式

内模式(也称存储模式)

  • 是数据物理结构和存储方式的描述
  • 是数据在数据库内部的表示方式
    • 记录的存储方式(例如,顺序存储,堆存储,hash存储等)
    • 索引的组织方式
    • 数据是否压缩存储
    • 数据是否加密
    • 数据存储记录结构的规定
      一个数据库只有一个内模式
数据库的二级映像与数据独立性

三级模式是对数据的三个抽象级别
二级映象在数据库管理系统内部实现这三个抽象层次的联系和转换

  • 外模式/模式映像
  • 模式/内模式映像
外模式/模式映像

模式:描述的是数据的全局逻辑结构
外模式:描述的是数据的局部逻辑结构
同一个模式可以有任意多个外模式
每一个外模式,数据库系统都有一个外模式/模式映象,定义外模式与模式之间的对应关系
映象定义通常包含在各自外模式的描述中
保证数据的逻辑独立性

  • 当模式改变时,数据库管理员对外模式/模式映象作相应改变,使外模式保持不变
  • 应用程序是依据数据的外模式编写的,应用程序不必修改,保证了数据与程序的逻辑独立性,简称数据的逻辑独立性
模式/内模式映像

模式/内模式映象定义了数据全局逻辑结构与存储结构之间的对应关系。

  • 例如,说明逻辑记录和字段在内部是如何表示的
    数据库中模式/内模式映象是唯一的
    该映象定义通常包含在模式描述中
    保证数据的物理独立性
  • 当数据库的存储结构改变了(例如选用了另一种存储结构),数据库管理员修改模式/内模式映象,使模式保持不变。
  • 应用程序不受影响。保证了数据与程序的物理独立性,简称数据的物理独立性。
数据库的二级映像

数据库模式:

  • 即全局逻辑结构是数据库的中心与关键
  • 独立于数据库的其他层次
  • 设计数据库模式结构时应首先确定数据库的逻辑模式

数据库的内模式:

  • 依赖于它的全局逻辑结构
  • 独立于数据库的用户视图,即外模式
  • 独立于具体的存储设备
  • 将全局逻辑结构中所定义的数据结构及其联系按照一定的物理存储策略进行组织,以达到较好的时间与空间效率

数据库的外模式:

  • 面向具体的应用程序
  • 定义在逻辑模式之上
  • 独立于存储模式和存储设备
  • 当应用需求发生较大变化,相应外模式不能满足其视图要求时,该外模式就得做相应改动
  • 设计外模式时应充分考虑到应用的扩充性

特定的应用程序:

  • 在外模式描述的数据结构上编制的
  • 依赖于特定的外模式
  • 与数据库的模式和存储结构独立
  • 不同的应用程序有时可以共用同一个外模式

数据库的二级映像:

  • 保证了数据库外模式的稳定性
  • 从底层保证了应用程序的稳定性,除非应用需求本身发生变化,否则应用程序一般不需要修改
    数据与程序之间的独立性,使得数据的定义和描述可以从应用程序中分离出去
    数据的存取由数据库管理系统管理
  • 简化了应用程序的编制
  • 大大减少了应用程序的维护和修改

数据库系统的组成

软硬件平台、人员(重点为数据库管理员、系统分析员/数据库设计人员、应用程序员/最终用户)

Part2——关系

关系、关系模式和关系数据库

域是一组具有相同数据类型的值的集合,类似于高中的函数作用域吧

笛卡尔积

定义

前置条件:给定一组域D1,D2,…,Dn,允许其中某些域是相同的。
定义:上述域的笛卡尔积为D1*D2*...*Dn = {(d1,d2,…,dn)|di∈Di,i=1,2,…,n}
可以看出所谓笛卡尔积就是所有域的所有取值的一个组合
不能重复,集合的唯一性?

一些概念

元组:笛卡尔积中每一个元素(d1,d2,…,dn)叫作一个n元组(n-tuple)或简称元组

分量:笛卡尔积元素(d1,d2,…,dn)中的每一个值di叫作一个分量

基数:若Di(i=1,2,…,n)为有限集,其基数为mi (i = 1,2,… ,n),则D1×D2×…×Dn的基数M为:m1*m2*...mn,就是排列组合的总数?无需考虑是否重复。

笛卡尔积表示方法:

  • 笛卡尔积可表示为一张二维表
  • 表中每行对应一个元组,表中每列对应一个域

说起二维表,就有关系数据库那味了

例如,给出3个域:
D1=导师集合SUPERVISOR={张清玫,刘逸}
D2=专业集合SPECIALITY={计算机专业,信息专业}
D3=研究生集合POSTGRADUATE={李勇,刘晨,王敏}
D1,D2,D3的笛卡尔积(其基数为2×2×3=12)为
D1×D2×D3={  (张清玫,计算机专业,李勇),(张清玫,计算机专业,刘晨), (张清玫,计算机专业,王敏),(张清玫,信息专业,李勇),(张清玫,信息专业,刘晨),(张清玫,信息专业,王敏),(刘逸,计算机专业,李勇),(刘逸,计算机专业,刘晨), (刘逸,计算机专业,王敏),(刘逸,信息专业,李勇), (刘逸,信息专业,刘晨),(刘逸,信息专业,王敏) }

给上述笛卡尔积就可以列出相对应的表格:

SUPERVISORSPECIALITYPOSTGRADUATE
张清玫计算机专业李勇
张清玫计算机专业刘晨
张清玫计算机专业王敏
张清玫信息专业李勇
张清玫信息专业刘晨
张清玫信息专业王敏
刘逸计算机专业李勇
刘逸计算机专业刘晨
刘逸计算机专业王敏
刘逸信息专业李勇
刘逸信息专业刘晨
刘逸信息专业王敏

关系

一些概念

关系(Relation):

  • D1×D2×…×Dn的子集叫作在域D1,D2,…,Dn上的关系,表示为R(D1,D2,…,Dn)
    • R :关系名
    • n :关系的目或度(Degree)
      • 当n=1时,称该关系为单元关系(Unary relation)或一元关系
      • 当n=2时,称该关系为二元关系(Binary relation)

关系中的每个元素是关系中的元组,通常用t表示。

关系的表示:关系也是一个二维表,表的每行对应一个元组,表的每列对应一个域

属性:

  • 关系中不同列可以对应相同的域,回溯到笛卡尔积定义时允许某些域是相同的
  • 为了加以区分,必须对每列起一个名字,称为属性(Attribute),即使是相同的域,此刻也有了不同的属性含义
  • n目关系必有n个属性

码:

  • 候选码(Candidate key)
    • 若关系中的某一属性组的值能唯一地标识一个元组,而其子集不能,则称该属性组为候选码
    • 简单的情况:候选码只包含一个属性
    • 最极端的情况:关系模式的所有属性组是这个关系模式的候选码,称为全码(All-key)
  • 主码
    • 若一个关系有多个候选码,则选定其中一个为主码(Primary key)
  • 主属性
    • 候选码的诸属性称为主属性(Prime attribute)
    • 不包含在任何侯选码中的属性称为非主属性(Non-Prime attribute)或非码属性(Non-key attribute)
基本关系

基本关系:

  • 实际存在的表,是实际存储数据的逻辑表示

基本关系的性质:

  • 列是同质的(Homogeneous)
    • 每一列中的分量是同一类型的数据,来自同一个域
  • 不同的列可出自同一个域
    • 其中的每一列称为一个属性
    • 不同的属性要给予不同的属性名
  • 列的顺序无所谓
    • 列的次序可以任意交换
      任意两个元组的候选码不能相同,都说了是候选码了,怎么可能相同?
  • 行的顺序无所谓
    • 行的次序可以任意交换
      分量必须取原子值

查询表:

  • 查询结果对应的表

视图表:

  • 由基本表或其他视图表导出的表,是虚表,不对应实际存储的数据

关系模式

定义
  • 关系模式是型,关系是值
  • 关系模式是对关系的描述
    • 元组集合的结构
      • 属性构成
      • 属性来自的域
      • 属性与域之间的映像关系
  • 规定了完整性约束条件
表示
  • 关系模式可以形式化地表示为:R(U,D,DOM,F)
    • R:关系名
    • U:组成该关系的属性名集合
    • D:U中属性所来自的域
    • DOM :属性向域的映象集合
    • F :属性间数据的依赖关系的集合
  • 关系模式通常可以简记为 R (U) 或 R (A1,A2,…,An),在之前关系的定义上稍微缩小了点范围
    • R : 关系名
    • A1,A2,…,An : 属性名
      • 注:域名及属性向域的映象常常直接说明为属性的类型、长度

关系模式与关系

  • 关系模式
    • 对关系的描述,型
    • 静态、稳定
  • 关系
    • 关系模式在某一时刻的状态或内容,值
    • 动态、变化
  • 关系模式和关系往往统称为关系
    • 通过上下文加以区分,还真是有够笼统的

关系数据库

  • 关系数据库
    • 在一个给定的应用领域中,所有关系的集合构成一个关系数据库
  • 关系数据库的型与值
    • 关系数据库的型: 关系数据库模式,是对关系数据库的描述
    • 关系数据库的值: 关系模式在某一时刻对应的关系的集合,通常称为关系数据库

关系的完整性

概述

关系的三类完整性约束:

  • 实体完整性和参照完整性,不变
    • 关系模型必须满足的完整性约束条件,称为关系的两个不变性,应该由关系系统自动支持
  • 用户定义的完整性,变
    • 应用领域需要遵循的约束条件,体现了具体领域中的语义约束

实体完整性

实体完整性规则(Entity Integrity)

  • 若属性A是基本关系R的主属性,则属性A不能取空值
  • 空值就是“不知道”或“不存在”或“无意义”的值

例:

  • 选修(学号,课程号,成绩)
    • “学号、课程号”为主码
    • “学号”和“课程号”两个属性都不能取空值

实体完整性规则的说明:

  • 实体完整性规则是针对基本关系而言的。一个基本表通常对应现实世界的一个实体集。
  • 现实世界中的实体是可区分的,即它们具有某种唯一性标识。
  • 关系模型中以主码作为唯一性标识。
  • 主码中的属性即主属性不能取空值。
    • 主属性取空值,就说明存在某个不可标识的实体,即存在不可区分的实体,这与第2点相矛盾,因此这个规则称为实体完整性

参照完整性

关系间的引用

在关系模型中实体及实体间的联系都是用关系来描述的,自然存在着关系与关系间的引用。

关系间、关系内部一对多、多对多的联系

例1 :学生实体、专业实体以及专业与学生间的一对多联系,学生关系引用了专业关系的主码“专业号”。
学生(学号,姓名,性别,专业号,年龄)
专业(专业号,专业名)

例2:学生、课程、学生与课程之间的多对多联系,选修关系引用了学生关系的主码“学号”和课程关系的主码“课程号”。

  • 学生(学号,姓名,性别,专业号,年龄)
  • 课程(课程号,课程名,学分)
  • 选修(学号课程号,成绩)

例3:学生实体及其内部的领导联系(一对多),学生关系引用了自身的主码“学号”
学生(学号,姓名,性别,专业号,年龄,班长

外码

设F是基本关系R的一个或一组属性,但不是关系R的码。如果F与基本关系S的主码Ks相对应,则称F是R的外码(Foreign Key)

  • 基本关系R称为参照关系(Referencing Relation)
  • 基本关系S称为被参照关系(Referenced Relation)或目标关系(Target Relation)
    其中
  • 关系R和S不一定是不同的关系
  • 目标关系S的主码Ks和参照关系的外码F必须定义在同一个(或一组)域上
  • 外码并不一定要与相应的主码同名
    • 当外码与相应的主码属于不同关系时,往往取相同的名字,以便于识别

例1中,学生关系的“专业号”与专业关系的主码“专业号”相对应

  • “专业号”属性是学生关系的外码
  • 专业关系是被参照关系,学生关系为参照关系

例2中,选修关系的“学号” 与学生关系的主码“学号”相对应,选修关系的“课程号”与课程关系的主码“课程号”相对应

  • “学号”和“课程号”是选修关系的外码
  • 学生关系和课程关系均为被参照关系
  • 选修关系为参照关系

例3中,“班长”与本身的主码“学号”相对应

  • “班长”是外码
  • 学生关系既是参照关系也是被参照关系
  • 对应外码描述中R和S不一定是不同的关系
参照完整性规则

参照完整性规则:

  • 若属性(或属性组)F是基本关系R的外码,它与基本关系S的主码Ks相对应(基本关系R和S不一定是不同的关系),则对于R中每个元组在F上的值必须为:
    • 或者取空值(F的每个属性值均为空值)
    • 或者等于S中某个元组的主码值

例1中,学生关系中每个元组的“专业号”属性只取两类值:

  • 空值,表示尚未给该学生分配专业
  • 非空值,这时该值必须是专业关系中某个元组的“专业号”值,表示该学生不可能分配一个不存在的专业

例2中,选修(学号课程号,成绩)“学号”和“课程号”可能的取值 :

  • 选修关系中的主属性,不能取空值
  • 只能取相应被参照关系中已经存在的主码值
  • 这个例子看着还挺迷糊的,上面介绍外码把这哥俩推出去,现在又说这两合起来是主码,不能取空值,不过也确实符合定义,分开来确实不是主码,可以是外码,合起来作为主码自然不能取空值

例3中,学生(学号,姓名,性别,专业号,年龄,班长),“班长”属性值可以取两类值:

  • 空值,表示该学生所在班级尚未选出班长
  • 非空值,该值必须是本关系中某个元组的学号值

用户定义的完整性

针对某一具体关系数据库的约束条件,反映某一具体应用所涉及的数据必须满足的语义要求
关系模型应提供定义和检验这类完整性的机制,以便用统一的系统的方法处理它们,而不需由应用程序承担这一功能

就是人为根据需求对于数据进行的要求

例:课程(课程号,课程名,学分)

  • “课程号”属性必须取唯一值
  • 非主属性“课程名”也不能取空值
  • “学分”属性只能取值{1,2,3,4}

关系操作和关系代数

关系操作

跟之前数据操作其实是一样的,常见的关系操作:

  • 查询操作:选择、投影、连接、除、并、差、交、笛卡尔积
    • 选择、投影、并、差、笛卡尔积是5种基本操作
  • 数据更新:插入、删除、修改

关系操作特点:

  • 集合操作方式:操作的对象和结果都是集合,一次一集合的方式

关系代数

关系代数是一种抽象的查询语言,它用对关系的运算来表达查询

  • 运算对象是关系
  • 运算结果亦为关系
  • 关系代数的运算符有两类:集合运算符和专门的关系运算符
    • 传统的集合运算是从关系的“水平”方向,即行的角度进行
    • 专门的关系运算不仅涉及行而且涉及列

只做查询操作?数据的更新这些都不算是关系代数吗?不过看关系代数运算符,确实如此。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7n1cAdet-1687783560167)(null)]

使用的记号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3m24hQEI-1687783554817)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMpVWb8l-1687783558122)(null)]
所以这元组的连接这么随意吗?没有共同的码就可以直接连接起来吗?
象集这玩意也就是元组某一取值确定时,另一属性的取值,类似于函数确定了x取值,去求y取值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LrIo8Lgp-1687783562350)(null)]

并(Union)

条件:
R和S具有相同的目n(即两个关系都有n个属性),相应的属性取自同一个域

定义:
R∪S ,仍为n目关系,由属于R或属于S的元组组成,跟集合并操作其实没什么差别

差(Difference)

条件:
R和S具有相同的目n,相应的属性取自同一个域

定义:
R - S ,仍为n目关系,由属于R而不属于S的所有元组组成

条件:
R和S具有相同的目n,相应的属性取自同一个域

定义:
R∩S,仍为n目关系,由既属于R又属于S的元组组成

笛卡尔积

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D00hokjr-1687783557012)(null)]

与最原始的关于笛卡尔积的定义还是很符合的,可以将其抽象为两个域上的笛卡尔积

选择

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gVQpvkmy-1687783552648)(null)]

选择运算是从关系R中选取使逻辑表达式F为真的元组,是从行的角度进行的运算,select语句

来看个例子吧:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfsQz3wZ-1687783553977)(null)]

要注意F就是这里的Sdept = 'IS',我之前以为=是连接左右两个大部分的,其实不是哈,所以F确实是一个逻辑表达式,括号里为进行选择操作的关系集合

投影

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJwQaWOD-1687783563737)(null)]

来看个例子吧:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPQCNPKD-1687783557867)(null)]

A是这里的Sname,Sdept,括号里为进行操作的关系,注意下取消重复行感觉也没什么吧。

连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btDPXZ4M-1687783551537)(null)]

这玩意定义的是真复杂,这里也是用到了先前所看到的“连接”操作,其实就是将两个元组拼接起来。是相较于原先连接操作更加规范化的一种连接,注意A和B要是可比的。

两种较为特殊的连接:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xcmp9f79-1687783558944)(null)]

自然连接与我之前的疑问相吻合了,要有相同的码,并且将该相同的码合并起来。

  • 一般的连接操作是从行的角度进行运算
  • 自然连接还需要取消重复列,所以是同时从行和列的角度进行运算

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5zsMh3x4-1687783549901)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkYI7Oxj-1687783556803)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yivvVEIk-1687783557222)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qac0Bl6v-1687783563095)(null)]

突然发觉这答案貌似不太对吧,b2,2这组数据也符合上述等值连接和自然连接啊。课程网站通知说课件10有点问题,可我下载的就是更改后的?结合后续题目,源数据应该为b5,2,我来帮他手动改一下吧。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bzSwAraI-1687783559957)(null)]

注意自然连接的写法最为特殊,当然解决自然连接可以先去求解等值连接,而后去除重复列即可

外连接

悬浮元组(Dangling tuple)

  • 两个关系R和S在做自然连接时,关系R中某些元组有可能在S中不存在公共属性上值相等的元组,从而造成R中这些元组在操作时被舍弃了,这些被舍弃的元组称为悬浮元组。

外连接(Outer Join)

  • 如果把悬浮元组也保存在结果关系中,而在其他属性上填空值(Null),就叫做外连接
  • 左外连接(LEFT OUTER JOIN或LEFT JOIN)
    • 只保留左边关系R中的悬浮元组
  • 右外连接(RIGHT OUTER JOIN或RIGHT JOIN)
    • 只保留右边关系S中的悬浮元组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sed8LRxa-1687783560648)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O3ZvWegp-1687783556401)(null)]

除运算

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZYeOHBs-1687783550558)(null)]

除操作是同时从行和列角度进行运算,说的玄乎,其实就是得到的结果行和列数都改变了

感觉确实也挺像除法的定义的,毕竟关系我们可以将其看作笛卡尔积,也就是某种乘法操作,如今只是某种程度上可以说是反过来,但感觉表述好像总是有些问题…

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9F6QHzH-1687783554609)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LPCuYXL4-1687783551337)(null)]

Part3——SQL

该章节配合前面的MySQL一起食用极佳,毕竟MySQL的sql语句与课上所学差别不大,无奈本章是较为重要考点,不过相信出的sql语句不会很难

SQL概述

SQL(Structured Query Language)结构化查询语言,是关系数据库的标准语言
SQL是一个通用的、功能极强的关系数据库语言

  • 综合统一
  • 高度非过程化
  • 面向集合的操作方式
  • 以同一种语法结构提供两种使用方法
  • 语言简洁,易学易用

SQL特点

综合统一:

  • 集数据定义语言(DDL),数据操纵语言(DML),数据控制语言(DCL)功能于一体。
  • 可以独立完成数据库生命周期中的全部活动:
    • 定义和修改、删除关系模式,定义和删除视图,插入数据,建立数据库;
    • 对数据库中的数据进行查询和更新;
    • 数据库重构和维护
    • 数据库安全性、完整性控制,以及事务控制
    • 嵌入式SQL和动态SQL定义
  • 用户数据库投入运行后,可根据需要随时逐步修改模式,不影响数据库的运行。
  • 数据操作符统一

高度非过程化:

  • 非关系数据模型的数据操纵语言“面向过程”,必须指定存取路径。
  • SQL只要提出“做什么”,无须了解存取路径。
  • 存取路径的选择以及SQL的操作过程由系统自动完成。

面向集合的操作方式:

  • 非关系数据模型采用面向记录的操作方式,操作对象是一条记录
  • SQL采用集合操作方式
    • 操作对象、查找结果可以是元组的集合
    • 一次插入、删除、更新操作的对象可以是元组的集合

以同一种语法结构提供多种使用方式:

  • SQL是独立的语言,能够独立地用于联机交互的使用方式
  • SQL又是嵌入式语言,能够嵌入到高级语言(例如C,C++,Java)程序中,供程序员设计程序时使用

语言简洁,易学易用:

  • SQL功能极强,完成核心功能只用了9个动词:
    • 数据定义:CREATE,DROP,ALTER
    • 数据查询:SELECT
    • 数据操作:INSERT,UPDATE,DELETE
    • 数据控制:GRANT,REVOKE
  • 接下来学习也将围绕这几个动词展开

SQL与关系数据库三级模式

内模式对应于存储文件,模式对应于基本表,外模式对应于视图

基本表:

  • 本身独立存在的表
  • 一个关系对应一个基本表
  • 一个(或多个)基本表对应一个存储文件
  • 一个表可以带若干索引

存储文件:

  • 逻辑结构组成了关系数据库的内模式
  • 物理结构对用户是隐蔽的

视图:

  • 从一个或几个基本表导出的表
  • 数据库中只存放视图的定义而不存放视图对应的数据
  • 视图是一个虚表
  • 用户可以在视图上再定义视图

SQL数据定义

一个关系数据库管理系统的实例(Instance)中可以建立多个数据库
~~一个数据库中可以建立多个模式,~~这句话与模式章节ppt内容相矛盾,查阅资料,认为一个数据库只能有一个模式,可以有多个外模式
一个模式下通常包括多个表、视图和索引等数据库对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xcKA5LxN-1687783556600)(null)]

定义模式

定义模式实际上定义了一个命名空间。

  • 在这个空间中可以定义该模式包含的数据库对象,例如基本表、视图、索引等。
    在CREATE SCHEMA中可以接受CREATE TABLE,CREATE VIEW和GRANT子句。
  • CREATE SCHEMA <模式名> AUTHORIZATION <用户名> [<表定义子句>| <视图定义子句>|<授权定义子句>]

跟学习MySQL的CREATE DATABASE指令其实很像,网上对于两者的区别也是描述含糊,一种较为主流的说法,Mysql文档说:CREATE SCHEMA是自MySQL 5.0.2起CREATE DATABASE的同义词。所以我就纳闷,一个数据库难道还能有多个databases?

[3.1] 为用户WANG定义一个学生-课程模式S-T
CREATE SCHEMA “S_T” AUTHORIZATION WANG;
[3.2] CREATE SCHEMA AUTHORIZATION WANG;
该语句没有指定<模式名><模式名>隐含为<用户名>
[3.3]为用户ZHANG创建了一个模式TEST,并且在其中定义一个表TAB1
CREATE SCHEMA TEST AUTHORIZATION ZHANG
CREATE TABLE TAB1   ( COL1 SMALLINT, 
 	                 COL2 INT,
                     COL3 CHAR(20),
                     COL4 NUMERIC(10,3),
                     COL5 DECIMAL(5,2));

删除模式

DROP SCHEMA <模式名> <CASCADE|RESTRICT>

  • CASCADE(级联)
    • 删除模式的同时把该模式中所有的数据库对象全部删除
  • RESTRICT(限制)
    • 如果该模式中定义了下属的数据库对象(如表、视图等),则拒绝该删除语句的执行。
    • 仅当该模式中没有任何下属的对象时才能执行。
[3.4]  DROP SCHEMA ZHANG CASCADE;
删除模式ZHANG
同时该模式中定义的表TAB1也被删除

定义基本表

CREATE TABLE <表名>
      (<列名> <数据类型>[ <列级完整性约束条件> ]
      [,<列名> <数据类型>[ <列级完整性约束条件>] ][,<表级完整性约束条件> ] );
<表名>:所要定义的基本表的名字
<列名>:组成该表的各个属性(列)
<列级完整性约束条件>:涉及相应属性列的完整性约束条件
<表级完整性约束条件>:涉及一个或多个属性列的完整性约束条件 
如果完整性约束条件涉及到该表的多个属性列,则必须定义在表级上,否则既可以定义在列级也可以定义在表级。 
[3.5]  建立“学生”表Student。学号是主码,姓名取值唯一。
CREATE TABLE Student          
      (Sno   CHAR(9) PRIMARY KEY,/* 列级完整性约束条件,Sno是主码*/                  
        Sname CHAR(20) UNIQUE,   /* Sname取唯一值*/
        Ssex    CHAR(2),
        Sage   SMALLINT,
        Sdept  CHAR(20)
      ); 
 [3.6 ] 建立一个“课程”表Course
          CREATE TABLE  Course
          (Cno       CHAR(4) PRIMARY KEY,
        	 Cname  CHAR(40) NOT NULL,            
         	 Cpno     CHAR(4),               	                      
            Ccredit  SMALLINTFOREIGN KEY (Cpno) REFERENCES  Course(Cno) 
          ); 
/* Cpno是外码
   被参照表是Course
   被参照列是Cno
*/
[3.7]  建立一个学生选课表SC
         CREATE TABLE  SC
          (Sno  CHAR(9), 
           Cno  CHAR(4),  
           Grade  SMALLINTPRIMARY KEY (Sno,Cno),  
                          /* 主码由两个属性构成,必须作为表级完整性进行定义*/
           FOREIGN KEY (Sno) REFERENCES Student(Sno),
                         /* 表级完整性约束条件,Sno是外码,被参照表是Student */
           FOREIGN KEY (Cno)REFERENCES Course(Cno)
                          /* 表级完整性约束条件, Cno是外码,被参照表是Course*/
        ); 

数据类型

SQL中的概念用数据类型来实现
定义表的属性时需要指明其数据类型及长度
选用哪种数据类型

  • 取值范围
  • 要做哪些运算

一些常见数据类型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62OOXrAy-1687783552844)(null)]

修改基本表

ALTER TABLE <表名>
[ ADD[COLUMN] <新列名> <数据类型> [ 完整性约束 ] ]
[ ADD <表级完整性约束>]
[ DROP [ COLUMN ] <列名> [CASCADE| RESTRICT] ]
[ DROP CONSTRAINT<完整性约束名>[ RESTRICT | CASCADE ] ]
[ALTER COLUMN <列名><数据类型> ] ;
-- <表名>是要修改的基本表
-- ADD子句用于增加新列、新的列级完整性约束条件和新的表级完整性约束条件
-- DROP COLUMN子句用于删除表中的列
-- 如果指定了CASCADE短语,则自动删除引用了该列的其他对象
-- 如果指定了RESTRICT短语,则如果该列被其他对象引用,关系数据库管理系统将拒绝删除该列
-- DROP CONSTRAINT子句用于删除指定的完整性约束条件
-- ALTER COLUMN子句用于修改原有的列定义,包括修改列名和数据类型
[3.8] 向Student表增加“入学时间”列,其数据类型为日期型
ALTER TABLE Student ADD S_entrance DATE;
不管基本表中原来是否已有数据,新增加的列一律为空值 
[3.9] 将年龄的数据类型由字符型(假设原来的数据类型是字符型)改为整数。
ALTER TABLE Student ALTER COLUMN Sage INT;
[3.10] 增加课程名称必须取唯一值的约束条件。
ALTER TABLE Course ADD UNIQUE(Cname);

删除基本表

DROP TABLE <表名>RESTRICT| CASCADE;
-- RESTRICT:删除表是有限制的。
-- 欲删除的基本表不能被其他表的约束所引用
-- 如果存在依赖该表的对象,则此表不能被删除

-- CASCADE:删除该表没有限制。
-- 在删除基本表的同时,相关的依赖对象一起删除
[3.11]  删除Student表
DROP TABLE  Student  CASCADE;
基本表定义被删除,数据被删除
表上建立的索引、视图、触发器等一般也将被删除 
[3.12] 若表上建有视图,选择RESTRICT时表不能删除;选择CASCADE时可以删除表,视图也自动删除。	
CREATE VIEW IS_Student      
  	AS 
	SELECT Sno,Sname,Sage
	FROM  Student
    WHERE Sdept='IS';
DROP TABLE Student RESTRICT;   
--ERROR: cannot drop table Student because other objects depend on it
DROP TABLE Student CASCADE; 	    
--NOTICE: drop cascades to view IS_Student

建立索引

建立索引的目的:加快查询速度

  • 由数据库管理员或表的拥有者建立
  • 由关系数据库管理系统自动完成维护
  • 关系数据库管理系统自动使用合适的索引作为存取路径,用户不必也不能显式地选择索引

关系数据库管理系统中常见索引:

  • 顺序文件上的索引
  • B+树索引
  • 散列(hash)索引
  • 位图索引
语句格式:
CREATE [UNIQUE] [CLUSTER] INDEX <索引名> 
ON <表名>(<列名>[<次序>][,<列名>[<次序>] ]);
<表名>:要建索引的基本表的名字
索引:可以建立在该表的一列或多列上,各列名之间用逗号分隔
<次序>:指定索引值的排列次序,升序:ASC,降序:DESC。缺省值:ASC
UNIQUE:此索引的每一个索引值只对应唯一的数据记录
CLUSTER:表示要建立的索引是聚簇索引
[3.13] 为学生-课程数据库中的Student,Course,SC三个表建立索引。Student表按学号升序建唯一索引,Course表按课程号升序建唯一索引,SC表按学号升序和课程号降序建唯一索引
CREATE UNIQUE INDEX  Stusno ON Student(Sno);
CREATE UNIQUE INDEX  Coucno ON Course(Cno);
CREATE UNIQUE INDEX  SCno ON SC(Sno ASC,Cno DESC);

修改/删除索引

ALTER INDEX <旧索引名> RENAME TO <新索引名>
[3.14] 将SC表的SCno索引名改为SCSno
	ALTER INDEX SCno RENAME TO SCSno;
DROP INDEX <索引名>;
删除索引时,系统会从数据字典中删去有关该索引的
描述。
[3.15]  删除Student表的Stusname索引
	        DROP INDEX Stusname;

数据字典

上述描述时引入了一个新的概念——数据字典

数据字典是关系数据库管理系统内部的一组系统表,它记录了数据库中所有定义信息:

  • 关系模式定义
  • 视图定义
  • 索引定义
  • 完整性约束定义
  • 各类用户对数据库的操作权限
  • 统计信息等
    关系数据库管理系统在执行SQL的数据定义语句时,实际上就是在更新数据字典表中的相应信息。

SQL数据查询

语句格式
    SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>]FROM <表名或视图名>[,<表名或视图名> ]|(SELECT 语句)      
        [AS]<别名>
        [ WHERE <条件表达式> ]
        [GROUP BY <列名1> [ HAVING <条件表达式> ] ]
        [ORDER BY <列名2> [ ASC|DESC ] ];

-- SELECT子句:指定要显示的属性列
-- FROM子句:指定查询对象(基本表或视图)
-- WHERE子句:指定查询条件
-- GROUP BY子句:对查询结果按指定列的值分组,该属性列值相等的元组为一个组。通常会在每组中作用聚集函数。
-- HAVING短语:只有满足指定条件的组才予以输出
-- ORDER BY子句:对查询结果表按指定列值的升序或降序排序 

简单SQL查询

查询指定列
[3.16]  查询全体学生的学号与姓名。
SELECT Sno,Sname FROM Student; 
[3.17]  查询全体学生的姓名、学号、所在系。
SELECT Sname,Sno,Sdept FROM Student;

查询全部列
[3.18]  查询全体学生的详细记录
SELECT Sno,Sname,Ssex,Sage,Sdept FROM Student;SELECT * FROM Student; 

查询经过计算的值

查询经过计算的值 
SELECT子句的<目标列表达式>不仅可以为表中的属性列,也可以是表达式
[3.19]  查全体学生的姓名及其出生年份。
SELECT Sname,2014-Sage          /*假设当时为2014年*/
FROM Student;
-- 2014-Sage是实实在在的表达式计算


[3.20] 查询全体学生的姓名、出生年份和所在的院系,要求用小写字母表示系名。
SELECT Sname,'Year of Birth: ',2014-Sage,LOWER(Sdept)
FROM Student;
-- 也有点函数那味了

使用列别名改变查询结果的列标题:
     SELECT Sname NAME,'Year of Birth:'  BIRTH,
      2014-Sage  BIRTHDAY,LOWER(Sdept)  DEPARTMENT
     FROM Student;
-- 将'Year of Birth'和'2014-Sage'均赋予了别名

选择表中的若干元组

[3.21]  查询选修了课程的学生学号。
    SELECT Sno FROM SC;
等价于:
	SELECT ALL  Sno  FROM SC;

消除取值重复的行

指定DISTINCT关键词,去掉表中重复的行 
如果没有指定DISTINCT关键词,则缺省为ALL 
SELECT DISTINCT Sno
FROM SC; 

条件查询语句

额,其实更应该强调的是where语句结构,而不是抱着一堆细小的谓词讲解

SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>]FROM <表名或视图名>[,<表名或视图名> ]|(SELECT 语句)      
        [AS]<别名>
        [ WHERE <条件表达式> ]

|查 询 条 件| 谓 词|
|比 较| =, >, <, >=, <=, !=, <>, !>, !<; NOT+上述比较运算符|
|确定范围| BETWEEN…AND…, NOT BETWEEN…AND…|
|确定集合| IN, NOT IN|
|字符匹配| LIKE, NOT LIKE|
|空 值| IS NULL, IS NOT NULL|
|多重条件(逻辑运算)| AND, OR, NOT|

比较
[3.22] 查询计算机科学系全体学生的名单。
    SELECT Sname
    FROM     Student
    WHERE  Sdept='CS'; 
[3.23]查询所有年龄在20岁以下的学生姓名及其年龄。
     SELECT Sname,Sage 
     FROM     Student    
     WHERE  Sage < 20;
[3.24]查询考试成绩有不及格的学生的学号。
SELECT DISTINCT Sn
FROM  SC
WHERE Grade<60; 
确定范围
谓词:   BETWEENANDNOT BETWEENAND[3.25] 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM     Student
WHERE   Sage BETWEEN 20 AND 23; 
[3.26]  查询年龄不在20~23岁之间的学生姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM    Student
WHERE Sage NOT BETWEEN 20 AND 23; 
确认集合
谓词:IN <值表>,  NOT IN <值表>
值表说是集合更好理解  
[3.27]查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
	SELECT Sname, Ssex
	FROM  Student
	WHERE Sdept IN ('CS','MA','IS' );
[3.28]查询既不是计算机科学系、数学系,也不是信息系的学生的姓名和性别。
	SELECT Sname, Ssex
	FROM Student
	WHERE Sdept NOT IN ('IS','MA’,'CS' );
字符匹配
谓词: [NOT] LIKE<匹配串>[ESCAPE<换码字符>]
<匹配串>可以是一个完整的字符串,也可以含有通配符%(任意长度(长度可以为0)的字符串)和 _(任意单个字符)
例如:a%b表示以a开头,以b结尾的任意长度的字符串
例如:a_b表示以a开头,以b结尾的长度为3的任意字符串

匹配串为固定字符串
[3.29]  查询学号为201215121的学生的详细情况。
     SELECT *    
     FROM  Student  
     WHERE  Sno LIKE '201215121';
等价于: 
      SELECT  * 
      FROM  Student 
      WHERE Sno = '201215121';

匹配串为含通配符的字符串
[3.30]  查询所有姓刘学生的姓名、学号和性别。
      SELECT Sname, Sno, Ssex
      FROM Student
      WHERE  Sname LIKE '刘%';
[3.31]  查询姓"欧阳"且全名为三个汉字的学生的姓名。
      SELECT Sname
      FROM   Student
      WHERE  Sname LIKE '欧阳__';
[3.32]  查询名字中第2个字为"阳"字的学生的姓名和学号。
  SELECT Sname,Sno
  FROM     Student
  WHERE  Sname LIKE '__阳%';
[3.33]  查询所有不姓刘的学生姓名、学号和性别。
  SELECT Sname, Sno, Ssex
  FROM     Student
  WHERE  Sname NOT LIKE '刘%';

使用换码字符将通配符转义为普通字符
ESCAPE '\' 表示“\” 为换码字符,下面的例子将'_'转化为普通字符
 [3.34]  查询DB_Design课程的课程号和学分。
      SELECT Cno,Ccredit
      FROM     Course
      WHERE  Cname LIKE 'DB\_Design' ESCAPE '\' ;
[例3.35]  查询以"DB_"开头,且倒数第3个字符为 i的课程的详细情况。
      SELECT  *
      FROM    Course
      WHERE  Cname LIKE  'DB\_%i_ _' ESCAPE '\' ;
空值查询
谓词: IS NULLIS NOT NULLIS” 不能用 “=” 代替
[3.36]  某些学生选修课程后没有参加考试,所以有选课记录,但没有考试成绩。查询缺少成绩的学生的学号和相应的课程号。
      SELECT Sno,Cno
      FROM    SC
      WHERE  Grade IS NULL
[3.37]  查所有有成绩的学生学号和课程号。
      SELECT Sno,Cno
      FROM     SC
      WHERE  Grade IS NOT NULL;
多重条件查询
逻辑运算符:ANDOR来连接多个查询条件
 AND的优先级高于ORAND优先级高于OR还是第一次看到
 可以用括号改变优先级
[3.38]  查询计算机系年龄在20岁以下的学生姓名。
SELECT Sname
FROM  Student
WHERE Sdept= 'CS' AND Sage<20;
[3.27]  查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT Sname, Ssex
FROM     Student
WHERE  Sdept IN ('CS ','MA ','IS')
可改写为:
SELECT Sname, Ssex
FROM     Student
WHERE  Sdept= ' CS' OR Sdept= ' MA' OR Sdept= 'IS ';

查询结果排序

ORDER BY子句
可以按一个或多个属性列排序
升序:ASC;降序:DESC;缺省值为升序
对于空值,排序时显示的次序由具体系统实现来决定

[3.39]查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
        SELECT Sno, Grade
        FROM    SC
        WHERE  Cno= ' 3 '
        ORDER BY Grade DESC;
[3.40]查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
        SELECT  *
        FROM  Student
        ORDER BY Sdept, Sage DESC;

聚集函数

统计元组个数

  • COUNT(*)

统计一列中值的个数

  • COUNT([DISTINCT|ALL] <列名>)

计算一列值的总和(此列必须为数值型)

  • SUM([DISTINCT|ALL] <列名>)

计算一列值的平均值(此列必须为数值型)

  • AVG([DISTINCT|ALL] <列名>)

求一列中的最大值和最小值

  • MAX([DISTINCT|ALL] <列名>)
  • MIN([DISTINCT|ALL] <列名>)
[3.41]  查询学生总人数。
	SELECT COUNT(*)
	FROM  Student; 
[3.42]  查询选修了课程的学生人数。
	SELECT COUNT(DISTINCT Sno)
	FROM SC;
[3.43]  计算1号课程的学生平均成绩。
	SELECT AVG(Grade)
	FROM    SC
    WHERE Cno= ' 1 ';
[3.44]  查询选修1号课程的学生最高分数。
    SELECT MAX(Grade)
    FROM SC
    WHERE Cno='1';
[3.45 ] 查询学生201215012选修课程的总学分数。
    SELECT SUM(Ccredit)
    FROM  SC,Course
    WHERE Sno='201215012' AND SC.Cno=Course.Cno; 

查询结果分组

GROUP BY子句分组:

  • 细化聚集函数的作用对象
    • 如果未对查询结果分组,聚集函数将作用于整个查询结果
    • 对查询结果分组后,聚集函数将分别作用于每个组
    • 按指定的一列或多列值分组,值相等的为一组
[3.46]  求各个课程号及相应的选课人数。
     SELECT Cno,COUNT(Sno)
     FROM    SC
     GROUP BY Cno;

HAVING短语与WHERE子句的区别:

  • 作用对象不同
  • WHERE子句作用于基表或视图,从中选择满足条件的元组
  • HAVING短语作用于组,从中选择满足条件的组。
[3.47]  查询选修了3门以上课程的学生学号。
     SELECT Sno
     FROM  SC
     GROUP BY Sno
     HAVING  COUNT(*) >3; 

[3.48 ]查询平均成绩大于等于90分的学生学号和平均成绩
因为WHERE子句中是不能用聚集函数作为条件表达式,下面的语句是不对的:
    SELECT Sno, AVG(Grade)
    FROM  SC
    WHERE AVG(Grade)>=90
    GROUP BY Sno;
正确的查询语句应该是:
    SELECT  Sno, AVG(Grade)
    FROM  SC
    GROUP BY Sno
    HAVING AVG(Grade)>=90;

连接查询

连接查询:同时涉及两个以上的表的查询
连接条件或连接谓词:用来连接两个表的条件

一般格式:
- [<表名1>.]<列名1>  <比较运算符>  [<表名2>.]<列名2>
- [<表名1>.]<列名1> BETWEEN [<表名2>.]<列名2> AND [<表名2>.]<列名3>

连接字段:连接谓词中的列名称

  • 连接条件中的各连接字段类型必须是可比的,但名字不必相同
等值连接

等值连接:连接运算符为=

[3.49]  查询每个学生及其选修课程的情况
SELECT  Student.*, SC.*
FROM     Student, SC
WHERE  Student.Sno = SC.Sno;
[3.50][3.49]用自然连接完成,去除相同列
SELECT  Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM     Student,SC
WHERE  Student.Sno = SC.Sno;
连接操作执行过程

随便看看就行了,也不难理解,也不太可能考

嵌套循环法(NESTED-LOOP)

  • 首先在表1中找到第一个元组,然后从头开始扫描表2,逐一查找满足连接件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。
  • 表2全部查找完后,再找表1中第二个元组,然后再从头开始扫描表2,逐一查找满足连接条件的元组,找到后就将表1中的第二个元组与该元组拼接起来,形成结果表中一个元组。
  • 重复上述操作,直到表1中的全部元组都处理完毕

排序合并法(SORT-MERGE),先排序后查找拼接

  • 常用于=连接
  • 首先按连接属性对表1和表2排序
  • 对表1的第一个元组,从头开始扫描表2,顺序查找满足连接条件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组。当遇到表2中第一条大于表1连接字段值的元组时,对表2的查询不再继续

索引连接(INDEX-JOIN)

  • 对表2按连接字段建立索引
  • 对表1中的每个元组,依次根据其连接字段值查询表2的索引,从中找到满足条件的元组,找到后就将表1中的第一个元组与该元组拼接起来,形成结果表中一个元组
同时进行选择和连接

一条SQL语句可以同时完成选择和连接查询,这时WHERE子句是由连接谓词和选择谓词组成的复合条件。

[3.51]查询选修2号课程且成绩在90分以上的所有学生的学号和姓名。
    SELECT Student.Sno, Sname
    FROM     Student, SC
    WHERE  Student.Sno=SC.Sno  AND    		               
    SC.Cno=' 2 ' AND SC.Grade>90;
执行过程:
先从SC中挑选出Cno='2'并且Grade>90的元组形成一个中间关系
再和Student中满足连接条件的元组进行连接得到最终的结果关系
自身连接

自身连接:一个表与其自己进行连接
需要给表起别名以示区别
由于所有属性名都是同名属性,因此必须使用别名前缀

[3.52]查询每一门课的间接先修课(即先修课的先修课)
    SELECT  FIRST.Cno, SECOND.Cpno
    FROM  Course  FIRST, Course  SECOND
    WHERE FIRST.Cpno = SECOND.Cno;
外连接

外连接与普通连接的区别

  • 普通连接操作只输出满足连接条件的元组
  • 外连接操作以指定表为连接主体,将主体表中不满足连接条件的元组一并输出
    • 左外连接
      • 列出左边关系中所有的元组
    • 右外连接
      • 列出右边关系中所有的元组
[3. 53] 改写[3.49]
    SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
    FROM  Student  LEFT OUT JOIN SC ON 
    (Student.Sno=SC.Sno);

还是回到最初连接章节对于外连接的表述

多表连接

多表连接:两个以上的表进行连接

[3.54]查询每个学生的学号、姓名、选修的课程名及成绩
   SELECT Student.Sno, Sname, Cname, Grade
   FROM    Student, SC, Course    /*多表连接*/
   WHERE Student.Sno = SC.Sno AND SC.Cno = Course.Cno;

嵌套查询

一个SELECT-FROM-WHERE语句称为一个查询块
将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询

     SELECT Sname	              /*外层查询/父查询*/
     FROM Student
     WHERE Sno IN
                        ( SELECT Sno        /*内层查询/子查询*/
                          FROM SC
                          WHERE Cno= ' 2 ');

  • 上层的查询块称为外层查询或父查询
  • 下层查询块称为内层查询或子查询
  • SQL语言允许多层嵌套查询
    • 即一个子查询中还可以嵌套其他子查询
  • 子查询的限制
    • 不能使用ORDER BY子句
  • 有些嵌套查询可以用连接运算替代
    • 谨慎使用嵌套查询

“谨慎使用嵌套查询”,有这句话就够了,下面看了图一乐

嵌套查询求解方法:

  • 不相关子查询:子查询的查询条件不依赖于父查询
    • 由里向外 逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
  • 相关子查询:子查询的查询条件依赖于父查询
    • 首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表
    • 然后再取外层表的下一个元组
    • 重复这一过程,直至外层表全部检查完为止
带有IN谓词的子查询
[3.55]  查询与“刘晨”在同一个系学习的学生。
此查询要求可以分步来完成
1.确定“刘晨”所在系名             
SELECT  Sdept  
FROM     Student                            
WHERE  Sname= ' 刘晨 ';
结果为: CS
2.查找所有在CS系学习的学生。    
SELECT   Sno, Sname, Sdept     
FROM      Student                 
WHERE   Sdept= ' CS '; 

将第一步查询嵌入到第二步查询的条件中
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept  IN
   (SELECT Sdept
    FROM Student
    WHERE Sname= ' 刘晨 ');
此查询为不相关子查询

用自身连接完成[3.55]查询要求
SELECT  S1.Sno, S1.Sname,S1.Sdept
FROM     Student S1,Student S2
WHERE  S1.Sdept = S2.Sdept  AND
S2.Sname = '刘晨';
带有比较运算符的子查询

当能确切知道内层查询返回单值时,可用比较运算符(>,<,=,>=,<=,!=或< >)。

[3.55]中,由于一个学生只可能在一个系学习,则可以用 = 代替INSELECT Sno,Sname,Sdept
     FROM    Student
     WHERE Sdept   =
                   (SELECT Sdept
                    FROM    Student
                    WHERE Sname= '刘晨');

[3.57 ]找出每个学生超过他选修课程平均成绩的课程号。
   SELECT Sno, Cno
    FROM    SC  x
    WHERE Grade >=(SELECT AVG(Grade) 
		            FROM  SC y
                    WHERE y.Sno=x.Sno);
带有ANY(SOME)或ALL谓词的子查询

使用ANY或ALL谓词时必须同时使用比较运算
语义为:

ANY 大于子查询结果中的某个值
ALL 大于子查询结果中的所有值
< ANY 小于子查询结果中的某个值
< ALL 小于子查询结果中的所有值
= ANY 大于等于子查询结果中的某个值
= ALL 大于等于子查询结果中的所有值
<= ANY 小于等于子查询结果中的某个值
<= ALL 小于等于子查询结果中的所有值
= ANY 等于子查询结果中的某个值
= ALL 等于子查询结果中的所有值(通常没有实际意义)
!=(或<>)ANY 不等于子查询结果中的某个值
!=(或<>)ALL 不等于子查询结果中的任何一个值

[3.58]  查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄
SELECT Sname,Sage
FROM    Student
WHERE Sage < ANY (SELECT  Sage
                    FROM    Student
                    WHERE Sdept= 'CS')
   AND Sdept <> 'CS' ;
   /*父查询块中的条件 */
-- 执行过程:
-- 首先处理子查询,找出CS系中所有学生的年龄,构成一个集合(20,19)
-- 处理父查询,找所有不是CS系且年龄小于 20 或19的学生

ANY和ALL,与中学时所学条件语句所有、存在相贴切,因而ANY(或SOME),ALL谓词与聚集函数、IN谓词的等价转换关系

带有EXISTS谓词的子查询

EXISTS谓词

  • 存在量词
  • 带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。
    • 若内层查询结果非空,则外层的WHERE子句返回真值
    • 若内层查询结果为空,则外层的WHERE子句返回假值
  • 由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。

NOT EXISTS谓词

  • 若内层查询结果非空,则外层的WHERE子句返回假值
  • 若内层查询结果为空,则外层的WHERE子句返回真值
[3.60]查询所有选修了1号课程的学生姓名。
思路分析:
本查询涉及Student和SC关系
在Student中依次取每个元组的Sno值,用此值去检查SC表
若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno=1’,则取此Student.Sname送入结果表
     SELECT Sname
     FROM Student
     WHERE EXISTS
                   (SELECT *
                    FROM SC
                    WHERE Sno=Student.Sno AND Cno= ' 1 ');
[3.61]  查询没有选修1号课程的学生姓名。
     SELECT Sname
     FROM     Student
     WHERE NOT EXISTS
                   (SELECT *
                    FROM SC
                    WHERE Sno = Student.Sno AND Cno='1');

不同形式的查询间的替换
一些带EXISTSNOT EXISTS谓词的子查询不能被其他形式的子查询等价替换
所有带IN谓词、比较运算符、ANYALL谓词的子查询都能用带EXISTS谓词的子查询等价替换
用EXISTS/NOT EXISTS实现全称量词
SQL语言中没有全称量词 (For all)
可以把带有全称量词的谓词转换为等价的带有存在量词的谓词

所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换

[3.55]查询与“刘晨”在同一个系学习的学生。
SELECT Sno,Sname,Sdept
    FROM Student
    WHERE Sdept  IN
        (SELECT Sdept
        FROM Student
        WHERE Sname= '刘晨');
可以用带EXISTS谓词的子查询替换:
 SELECT Sno,Sname,Sdept
     FROM Student S1
      WHERE EXISTS
                 (SELECT *
                     FROM Student S2
                     WHERE S2.Sdept = S1.Sdept AND
                             S2.Sname = '刘晨');

逻辑->离散关系代数->sql语句,看着容易,转换起来是真费劲

用EXISTS/NOT EXISTS实现逻辑蕴涵,不想看了

集合查询

集合操作的种类

  • 并操作UNION
  • 交操作INTERSECT
  • 差操作EXCEPT
    参加集合操作的各查询结果的列数必须相同;对应项的数据类型也必须相同
并操作

UNION:将多个查询结果合并起来时,系统自动去掉重复元组
UNION ALL:将多个查询结果合并起来时,保留重复元组

[3.64]  查询计算机科学系的学生及年龄不大于19岁的学生。
//局部
        SELECT *
        FROM Student
        WHERE Sdept= 'CS'
        UNION
        SELECT *
        FROM Student
        WHERE Sage<=19;

[3.65]  查询选修了课程1或者选修了课程2的学生。
SELECT Sno
FROM SC
WHERE Cno=' 1 '
UNION
SELECT Sno
FROM SC
WHERE Cno= ' 2 ';
交操作
[3.66]  查询计算机科学系的学生与年龄不大于19岁的学生的交集。
SELECT *
FROM Student
WHERE Sdept='CS' 
INTERSECT
SELECT *
FROM Student
WHERE Sage<=19 
[3.66] 实际上就是查询计算机科学系中年龄不大于19岁的学生。
SELECT *
FROM Student
WHERE Sdept= 'CS' AND  Sage<=19;
[3.67]查询既选修了课程1又选修了课程2的学生。
    SELECT Sno
    FROM SC
    WHERE Cno=' 1 ' 
    INTERSECT
    SELECT Sno
    FROM SC
    WHERE Cno='2 ‘;
[例3.67]也可以表示为:    
SELECT Sno
FROM    SC
WHERE Cno=' 1 ' AND Sno IN
(SELECT Sno
FROM SC
WHERE Cno=' 2 ');
差操作
[3.68]  查询计算机科学系的学生与年龄不大于19岁的学生的差集。
    SELECT *
    FROM Student
    WHERE Sdept='CS'
    EXCEPT
    SELECT  *
    FROM Student
    WHERE Sage <=19;
[3.68]实际上是查询计算机科学系中年龄大于19岁的学生
SELECT *
FROM Student
WHERE Sdept= 'CS' AND  Sage>19;

派生表查询

子查询不仅可以出现在WHERE子句中,还可以出现在FROM子句中,这时子查询生成的临时派生表(Derived Table)成为主查询的查询对象

[3.57]找出每个学生超过他自己选修课程平均成绩的课程号
    SELECT Sno, Cno
    FROM SC, (SELECT Sno, Avg(Grade) 
                FROM SC
    		   GROUP BY Sno)
                AS   Avg_sc(avg_sno,avg_grade)
    WHERE SC.Sno = Avg_sc.avg_sno
    and SC.Grade >=Avg_sc.avg_grade;

如果子查询中没有聚集函数,派生表可以不指定属性列,子查询SELECT子句后面的列名为其缺省属性。

[3.60]查询所有选修了1号课程的学生姓名,可以用如下查询完成:
    SELECT Sname
    FROM     Student,  
          (SELECT Sno FROM SC WHERE Cno=' 1 ') AS SC1/* 这里确实没有指定属性列名称*/
    WHERE  Student.Sno=SC1.Sno;

SQL数据更新

插入

将新元组插入指定表中,语句格式
INSERT
INTO <表名> [(<属性列1>[,<属性列2 >…)]
VALUES (<常量1> [,<常量2>]… );

  • INTO子句
    • 指定要插入数据的表名及属性列
    • 属性列的顺序可与表定义中的顺序不一致
    • 没有指定属性列:表示要插入的是一条完整的元组,且属性列属性与表定义中的顺序一致
    • 指定部分属性列:插入的元组在其余属性列上取空值
      VALUES子句
    • 提供的值必须与INTO子句匹配
    • 值的个数
    • 值的类型
[3.69]将一个新学生元组(学号:201215128;姓名:陈冬;性别:男;所在系:IS;年龄:18岁)插入到Student表中。
INSERT
INTO  Student (Sno,Sname,Ssex,Sdept,Sage)
VALUES ('201215128','陈冬','男','IS',18);
 [3.70]将学生张成民的信息插入到Student表中。
INSERT
INTO  Student
VALUES ('201215126','张成民','男',18,'CS'); 
[3.71] 插入一条选课记录( '200215128','1 ')。
INSERT
INTO SC(Sno,Cno)
VALUES ('201215128 ',' 1 ');
关系数据库管理系统将在新插入记录的Grade列上自动地赋空值。
或者:
INSERT
INTO SC
VALUES (' 201215128 ',' 1 ',NULL);
插入子查询

语句格式
INSERT
INTO <表名> [(<属性列1> [,<属性列2>… )]
子查询;
INTO子句
子查询

  • SELECT子句目标列必须与INTO子句匹配
    • 值的个数
    • 值的类型
[3.72]  对每一个系,求学生的平均年龄,并把结果存入数据库
第一步:建表
      CREATE  TABLE  Dept_age
          ( Sdept     CHAR(15)                           /*系名*/
            Avg_age SMALLINT);          	/*学生平均年龄*/
第二步:插入数据
        INSERT
       INTO  Dept_age(Sdept,Avg_age)
              SELECT  Sdept,AVG(Sage)
              FROM     Student
              GROUP BY Sdept;  

修改

语句格式
UPDATE <表名>
SET <列名>=<表达式>[,<列名>=<表达式>]…
[WHERE <条件>];
功能

  • 修改指定表中满足WHERE子句条件的元组
  • SET子句给出<表达式>的值用于取代相应的属性列
  • 如果省略WHERE子句,表示要修改表中的所有元组
修改一个元组:
[3.73]  将学生201215121的年龄改为22UPDATE  Student
         SET Sage=22
         WHERE  Sno=' 201215121';

修改多个元组
[3.74]  将所有学生的年龄增加1岁。
	    	 UPDATE Student
         	SET Sage= Sage+1;
修改子查询
 [3.75]  将计算机科学系全体学生的成绩置零。
        UPDATE SC
        SET     Grade=0
        WHERE Sno  IN
               (SELETE Sno
                FROM     Student
                WHERE  Sdept= 'CS' );
修改数据与完整性

关系数据库管理系统在执行修改语句时会检查修改操作是否破坏表上已定义的完整性规则

  • 实体完整性
  • 参照完整性
  • 用户定义的完整性
    • NOT NULL约束
    • UNIQUE约束
    • 值域约束

删除

语句格式
DELETE
FROM <表名>
[WHERE <条件>];
功能

  • 删除指定表中满足WHERE子句条件的元组

WHERE子句

  • 指定要删除的元组
  • 缺省表示要删除表中的全部元组,表的定义仍在字典中
删除一个元组:
[3.76]  删除学号为201215128的学生记录。
         DELETE
         FROM Student
         WHERE Sno= '201215128 ';
删除多个元组:
[3.77]  删除所有的学生选课记录。
        DELETE
        FROM SC;
删除子查询
[3.78]  删除计算机科学系所有学生的选课记录。
		DELETE
		FROM  SC
		WHERE  Sno  IN
			(SELETE  Sno
		            FROM   Student
		            WHERE  Sdept= 'CS') ;

SQL中的空值

空值的处理:
空值就是“不知道”或“不存在”或“无意义”的值。
一般有以下几种情况:

  • 该属性应该有一个值,但目前不知道它的具体值
  • 该属性不应该有值
  • 由于某种原因不便于填写
    尽可能不留空值

空值的产生

空值是一个很特殊的值,含有不确定性。对关系运算带来特殊的问题,需要做特殊的处理。

[3.79]向SC表中插入一个元组,学生号是”201215126”,课程号是”1”,成绩为空。
 INSERT INTO SC(Sno,Cno,Grade)
 VALUES('201215126 ','1',NULL);   /*该学生还没有考试成绩,取空值*/INSERT INTO SC(Sno,Cno)
 VALUES(' 201215126 ','1');             /*没有赋值的属性,其值为空值*/
[3.80]  将Student表中学生号为”201215200”的学生所属的系改为空值。
	UPDATE Student
	SET Sdept = NULL
	WHERE Sno='201215200';

空值的判断

判断一个属性的值是否为空值,用IS NULL或IS NOT NULL来表示,,在条件查询部分其实已经了解。

[3.81]  从Student表中找出漏填了数据的学生信息
	SELECT  *
	FROM Student
	WHERE Sname IS NULL OR Ssex IS NULL OR Sage IS NULL OR Sdept IS NULL;

空值的约束条件

属性定义(或者域定义)中

  • 有NOT NULL约束条件的不能取空值
  • 加了UNIQUE限制的属性不能取空值
  • 码属性不能取空值

空值的运算

空值与另一个值(包括另一个空值)的算术运算的结果为空值
空值与另一个值(包括另一个空值)的比较运算的结果为UNKNOWN。
有UNKNOWN后,传统二值(TRUE,FALSE)逻辑就扩展成了三值逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxQZaQYI-1687783551764)(null)]

[3.82]  找出选修1号课程的不及格的学生。
   SELECT Sno
   FROM SC
   WHERE Grade < 60 AND Cno='1';
  查询结果不包括缺考的学生,因为他们的Grade值为null//强调is null或is not null
[3.83]  选出选修1号课程的不及格的学生以及缺考的学生。
SELECT Sno
FROM SC
WHERE Grade < 60 AND Cno='1'
UNION
SELECT Sno
FROM SC
WHERE Grade IS NULL AND Cno='1'
或者
SELECT Sno
FROM SC
WHERE Cno='1' AND (Grade<60 OR Grade IS NULL);

视图

概念

视图的特点

  • 虚表,是从一个或几个基本表(或视图)导出的表
  • 只存放视图的定义,不存放视图对应的数据
  • 基表中的数据发生变化,从视图中查询出的数据也随之改变

由之前的学习可知,外模式对应视图

建立视图

语句格式
CREATE VIEW
<视图名> [(<列名> [,<列名>]…)]
AS <子查询>
[WITH CHECK OPTION];
WITH CHECK OPTION

  • 对视图进行UPDATE,INSERT和DELETE操作时要保证更新、插入或删除的行满足视图定义中的谓词条件(即子查询中的条件表达式)
  • 无需增删改,不需要check option
    子查询可以是任意的SELECT语句,是否可以含有ORDER BY子句和DISTINCT短语,则决定具体系统的实现。

组成视图的属性列名:全部省略或全部指定

  • 全部省略:
    • 由子查询中SELECT目标列中的诸字段组成
  • 明确指定视图的所有列名:
    • 某个目标列是聚集函数或列表达式
    • 多表连接时选出了几个同名列作为视图的字段
    • 需要在视图中为某个列启用新的更合适的名字

关系数据库管理系统执行CREATE VIEW语句时只是把视图定义存入数据字典,并不执行其中的SELECT语句。
在对视图查询时,按视图的定义从基本表中将数据查出。

建立单基表视图
[3.84]  建立信息系学生的视图。
CREATE VIEW IS_Student
        AS 
        SELECT Sno,Sname,Sage
        FROM     Student
        WHERE  Sdept= 'IS';

[3.85]建立信息系学生的视图,并要求进行修改和插入操作时仍需保证该视图只有信息系的学生 。
  CREATE VIEW IS_Student
  AS 
  SELECT Sno,Sname,Sage
  FROM  Student
  WHERE  Sdept= 'IS'
  WITH CHECK OPTION;

-- 定义IS_Student视图时加上了WITH CHECK OPTION子句,对该视图进行插入、修改和删除操作时,RDBMS会自动加上Sdept='IS'的条件。
-- 若一个视图是从单个基本表导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,我们称这类视图为行列子集视图。
-- IS_Student视图就是一个行列子集视图。
建立多基表视图
[3.86]  建立信息系选修了1号课程的学生的视图(包括学号、姓名、成绩)。
        CREATE VIEW IS_S1(Sno,Sname,Grade)
        AS 
        SELECT Student.Sno,Sname,Grade
        FROM  Student,SC
        WHERE  Sdept= 'IS' AND
                       Student.Sno=SC.Sno AND
                       SC.Cno= '1';
建立基于视图的视图
[3.87]  建立信息系选修了1号课程且成绩在90分以上的学生的视图。
        CREATE VIEW IS_S2
        AS
        SELECT Sno,Sname,Grade
        FROM  IS_S1
        WHERE  Grade>=90;
建立带表达式的视图
[3.88]  定义一个反映学生出生年份的视图。
        CREATE  VIEW BT_S(Sno,Sname,Sbirth)
        AS 
        SELECT Sno,Sname,2014-Sage
        FROM  Student;
建立分组视图
[3.89]  将学生的学号及平均成绩定义为一个视图
   CREAT  VIEW     S_G(Sno,Gavg)
             AS  
             SELECT Sno,AVG(Grade)
             FROM  SC
             GROUP BY Sno;
一些问题
[3.90]将Student表中所有女生记录定义为一个视图
      CREATE VIEW F_Student(F_Sno,name,sex,age,dept)
      AS
      SELECT  *    /*不指定属性列*/
      FROM  Student
      WHERE Ssex=‘女’;
缺点:
修改基表Student的结构后,Student表与F_Student视图的映象关系被破坏,导致该视图不能正确工作

删除视图

语句的格式:
DROP VIEW <视图名>[CASCADE];

  • 该语句从数据字典中删除指定的视图定义
  • 如果该视图上还导出了其他视图,使用CASCADE级联删除语句,把该视图和由它导出的所有视图一起删除
  • 删除基表时,由该基表导出的所有视图定义都必须显式地使用DROP VIEW语句删除
[3.91 ] 删除视图BT_S和IS_S1
	     DROP VIEW BT_S;	         /*成功执行*/
	     DROP VIEW IS_S1;	/*拒绝执行*/    
要删除IS_S1,需使用级联删除,IS_S1导出了IS_S2:
           DROP VIEW IS_S1 CASCADE; 

查询视图

用户角度:查询视图与查询基本表相同
关系数据库管理系统实现视图查询的方法

视图消解法(View Resolution)
  • 进行有效性检查
  • 转换成等价的对基本表的查询
  • 执行修正后的查询
[3.92]  在信息系学生的视图中找出年龄小于20岁的学生。
       SELECT   Sno,Sage
       FROM      IS_Student
       WHERE   Sage<20;
视图消解转换后的查询语句为:
 	SELECT  Sno,Sage       
 	FROM  Student
	WHERE  Sdept= 'IS'  AND  Sage<20;
[3.93]  查询选修了1号课程的信息系学生
SELECT  IS_Student.Sno,Sname
FROM     IS_Student,SC
WHERE  IS_Student.Sno =SC.Sno AND SC.Cno= '1';

视图消解法的局限:
有些情况下,视图消解法不能生成正确的查询。

错误:
SELECT Sno,AVG(Grade)
FROM     SC
WHERE  AVG(Grade)>=90
GROUP BY Sno;
正确:
SELECT  Sno,AVG(Grade)
FROM  SC
GROUP BY Sno
HAVING AVG(Grade)>=90;
或者
SELECT *
FROM  (SELECT Sno,AVG(Grade)
	       FROM  SC 
	       GROUP BY Sno) AS S_G(Sno,Gavg)
    WHERE Gavg>=90;

这不应该是转换设计者水平有限的原因吗?关我方法何干?

更新视图

[3.95]  将信息系学生视图IS_Student中学号”201215122”的学生姓名改为”刘辰”。
UPDATE  IS_Student
SET  Sname= '刘辰'
WHERE  Sno= ' 201215122 ';
转换后的语句:
UPDATE  Student
SET Sname= '刘辰'
WHERE Sno= ' 201215122 ' AND Sdept= 'IS';
[3.96]  向信息系学生视图IS_S中插入一个新的学生记录,其中学号为”201215129”,姓名为”赵新”,年龄为20INSERT
INTO IS_Student
VALUES(201215129,’赵新’,20);
转换为对基本表的更新:
INSERT
INTO   Student(Sno,Sname,Sage,Sdept)
VALUES('200215129 ','赵新',20,'IS' );
[3.97]删除信息系学生视图IS_Student中学号为”201215129”的记录 
DELETE
FROM IS_Student
WHERE Sno= ' 201215129 ';
转换为对基本表的更新:
DELETE
FROM Student
WHERE Sno= ' 201215129 ' AND Sdept= 'IS';

看来课件中所没有提及的重要一点:对视图进行更新会导致基本表中对应的内容同样发生更新

允许对行列子集视图进行更新
对其他类型视图的更新不同系统有不同限制

  • 更新视图的限制:一些视图是不可更新的,因为对这些视图的更新不能唯一地有意义地转换成对相应基本表的更新
    例:例3.89定义的视图S_G为不可更新视图。
    UPDATE S_G
    SET Gavg=90
    WHERE Sno= ‘201215121’;

这个对视图的更新无法转换成对基本表SC的更新,因为在创建视图时并没有WITH CHECK OPTION
一个不允许更新的视图上定义的视图也不允许更新

视图作用

当视图中数据不是直接来自基本表时,定义视图能够简化用户的操作,减少嵌套

  • 基于多张表连接形成的视图
  • 基于复杂嵌套查询的视图
  • 含导出属性的视图

视图机制能使不同用户以不同方式看待同一数据,适应数据库共享的需要

视图对重构数据库提供了一定程度的逻辑独立性:

例:学生关系Student(Sno,Sname,Ssex,Sage,Sdept) 
“垂直”地分成两个基本表:
	SX(Sno,Sname,Sage)        
	SY(Sno,Ssex,Sdept)
通过建立一个视图Student:
	CREATE VIEW  Student(Sno,Sname,Ssex,Sage,Sdept)
	AS  
    	SELECT  SX.Sno,SX.Sname,SY.Ssex,SX.Sage,SY.Sdept
   	FROM  SX,SY
  	WHERE  SX.Sno=SY.Sno;
使用户的外模式保持不变,用户的应用程序通过视图仍然能够查找数据
由于对视图的更新是有条件的,因此应用程序中修改数据的语句可能仍会因基本表结构的改变而改变。

视图能够对机密数据提供安全保护:
对不同用户定义不同视图,使每个用户只能看到他有权看到的数据

适当的利用视图可以更清晰的表达查询:

经常需要执行这样的查询“对每个同学找出他获得最高成绩的课程号”。
可以先定义一个视图,求出每个同学获得的最高成绩 
	 CREATE VIEW VMGRADE
          AS
          SELECT Sno, MAX(Grade)  Mgrade/*Mgrade是前者的属性重命名*/
          FROM  SC
          GROUP BY Sno;
然后用如下的查询语句完成查询:
     SELECT SC.Sno,Cno
     FROM SC,VMGRADE 
     WHERE SC.Sno=VMGRADE.Sno AND       
     SC.Grade=VMGRADE.Mgrade; 

剩下的章节除了NF,NoSQL部分会出考点,其他感觉也就是吹吹水,随便了解一下得了

Part4——数据库安全

数据库安全性

问题的提出

  • 数据库的一大特点是数据可以共享
  • 数据共享必然带来数据库的安全性问题
  • 数据库系统中的数据共享不能是无条件的共享
    • 军事秘密、国家机密、新产品实验数据、市场需求分析、市场营销策略、销售计划、客户档案、医疗档案、银行储蓄数据
  • 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。
  • 系统安全保护措施是否有效是数据库系统主要的性能指标之一。

果然,没有无限制的自由,就连数据库也不例外

不安全因素:

  • 非授权用户对数据库的恶意存取和破坏
    • 一些黑客(Hacker)和犯罪分子在用户存取数据库时猎取用户名和用户口令,然后假冒合法用户偷取、修改甚至破坏用户数据。
    • 数据库管理系统提供的安全措施主要包括用户身份鉴别、存取控制和视图等技术。
  • 数据库中重要或敏感的数据被泄露
    • 黑客和敌对分子千方百计盗窃数据库中的重要数据,一些机密信息被暴露。
    • 数据库管理系统提供的主要技术有强制存取控制、数据加密存储和加密传输等。
    • 审计日志分析
  • 安全环境的脆弱性
    • 数据库的安全性与计算机系统的安全性紧密联系
      • 计算机硬件、操作系统、网络系统等的安全性
    • 建立一套可信(Trusted)计算机系统的概念和标准

TCSEC/TDI安全级别划分:

  • 按系统可靠或可信程度逐渐增高
  • 各安全级别之间具有一种偏序向下兼容的关系
  • 正常的数据库其实要求等级也没必要那么高
安全级别定义
A1验证设计(Verified Design)
B3安全域(Security Domains)
B2结构化保护(Structural Protection)
B1标记安全保护(Labeled Security Protection)
C2受控的存取保护(Controlled Access Protection)
C1自主安全保护(Discretionary Security Protection)
D最小保护(Minimal Protection)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgNrVL1I-1687783555363)(null)]
注意一下这里面提出的两个概念,DAC和MAC,后面也会具体讲解,其他,额,看个乐呵,嗯,对

CC

  • 提出国际公认的表述信息技术安全性的结构
  • 把信息产品的安全要求分为
    • 安全功能要求
    • 安全保证要求

CC文本组成

  • 简介和一般模型
    • 有关术语、基本概念和一般模型以及与评估有关的一些框架
  • 安全功能要求
    • 列出了一系列类、子类和组件
  • 安全保证要求
    • 列出了一系列保证类、子类和组件
    • 提出了评估保证级(Evaluation Assurance Level,EAL),从EAL1至EAL7共分为七级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bEj18M90-1687783561969)(null)]

计算机系统的安全模型:
计算机系统中,安全措施是一级一级层层设置
系统根据用户标识鉴定用户身份,合法用户才准许进入计算机系统
数据库管理系统还要进行存取控制,只允许用户执行合法操作
操作系统有自己的保护措施
数据以密码形式存储到数据库中

用户标识和鉴别、数据库安全保护、操作系统安全保护、数据密码存储

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t463nvqq-1687783563476)(null)]
多层访问控制,难怪加上强制访问控制安全级别更高

存取控制

存取控制机制组成

  • 定义用户权限,并将用户权限登记到数据字典中
    • 用户对某一数据对象的操作权力称为权限
    • DBMS提供适当的语言来定义用户权限,存放在数据字典中,称做安全规则或授权规则
  • 合法权限检查
    • 用户发出存取数据库操作请求
    • DBMS查找数据字典,进行合法权限检查

用户权限定义合法权检查机制一起组成了数据库管理系统的存取控制子系统

DAC(自主存取控制)

  • C2级,我怎么记得前面明明白白说是C1级?
  • 用户对不同的数据对象有不同的存取权限
  • 不同的用户对同一对象也有不同的权限
  • 用户还可将其拥有的存取权限转授给其他用户
自主存取控制方法

通过 SQL 的GRANT 语句和REVOKE 语句实现

用户权限组成

  • 数据库对象
  • 操作类型

定义存取权限称为授权

  • 定义用户存取权限:定义用户可以在哪些数据库对象上进行哪些类型的操作

关系数据库系统中存取控制对象:

  • 数据库模式
    • 模式
    • 基本表
    • 视图
    • 索引
  • 数据
    • 基本表和视图
    • 属性列
SQL中授权机制
  • 数据库管理员:
    • 拥有所有对象的所有权限
    • 根据实际情况不同的权限授予不同的用户
  • 用户:
    • 拥有自己建立的对象的全部的操作权限
    • 可以使用GRANT,把权限授予其他用户
  • 被授权的用户
    • 如果具有“继续授权”的许可,可以把获得的权限再授予其他用户
  • 所有授予出去的权力在必要时又都可用REVOKE语句收回
GRANT语句

授予权限

GRANT语句的一般格式:
GRANT <权限>[,<权限>]…
ON <对象类型> <对象名>[,<对象类型> <对象名>]…
TO <用户>[,<用户>]…
[WITH GRANT OPTION];
语义:将对指定操作对象的指定操作权限授予指定的用户
发出GRANT:

  • 数据库管理员
  • 数据库对象创建者(即属主Owner)
  • 拥有该权限的用户

按受权限的用户

  • 一个或多个具体用户
  • PUBLIC(即全体用户)

WITH GRANT OPTION子句:

  • 指定:可以再授予
  • 没有指定:不能传播

不允许循环授权,我自己把权力下方下去结果获得权力的人来反过来给我授权,想想也不可能

[4.1] 把查询Student表权限授给用户U1
GRANT   SELECT 
ON   TABLE   Student 
TO   U1;
[4.2] 把对Student表和Course表的全部权限授予用户U2和U3
GRANT ALL PRIVILIGES 
ON TABLE Student,Course 
TO U2,U3;
[4.3] 把对表SC的查询权限授予所有用户
GRANT SELECT 
ON TABLE SC 
TO PUBLIC;
[4.4] 把查询Student表和修改学生学号的权限授给用户U4
GRANT UPDATE(Sno), SELECT 
ON TABLE Student 
TO U4;
对属性列的授权时必须明确指出相应属性列名 
[4.5] 把对表SC的INSERT权限授予U5用户,并允许他再将此权限授予其他用户
GRANT INSERT 
ON TABLE SC 
TO U5
WITH GRANT OPTION;
执行例4.5后,U5不仅拥有了对表SC的INSERT权限,还可以传播此权限:
[4.6] GRANT INSERT 
ON TABLE SC 
TO U6
WITH GRANT OPTION;
[4.7]同样,U6还可以将此权限授予U7, 但U7不能再传播此权限。
GRANT INSERT 
ON TABLE SC 
TO U7;
REVOKE语句

收回权限

授予的权限可以由数据库管理员或其他授权者用REVOKE语句收回
REVOKE语句的一般格式为:
REVOKE <权限>[,<权限>]…
ON <对象类型> <对象名>[,<对象类型><对象名>]…
FROM <用户>[,<用户>]…[CASCADE | RESTRICT];

[4.8] 把用户U4修改学生学号的权限收回
	REVOKE UPDATE(Sno)
	ON TABLE Student 
	FROM U4;
[4.9] 收回所有用户对表SC的查询权限
	REVOKE SELECT 
	ON TABLE SC 
	FROM PUBLIC;
[4.10] 把用户U5对SC表的INSERT权限收回
	REVOKE INSERT 
	ON TABLE SC 
	FROM U5 CASCADE ;
将用户U5的INSERT权限收回的时候使用CASCADE,则同时收回U6或U7的INSERT权限,否则拒绝执行该语句 
如果U6或U7还从其他用户处获得对SC表的INSERT权限,则他们仍具有此权限,系统只收回直接或间接从U5处获得的权限,别人给的我不管,管好自己
创建数据库模式的权限

数据库管理员在创建用户时实现
CREATE USER语句格式:
CREATE USER
[WITH][DBA|RESOURCE|CONNECT];

注: CREATE USER不是SQL标准,各个系统的实现相差甚远
只有系统的超级用户才有权创建一个新的数据库用户,说起来目前我也只用过超级用户

新创建的数据库用户有三种权限:CONNECT、RESOURCE和DBA
如没有指定创建的新用户的权限,默认该用户拥有CONNECT权限。拥有CONNECT权限的用户不能创建新用户,不能创建模式,也不能创建基本表,只能登录数据库,也就是说只能获得值
拥有RESOURCE权限的用户能创建基本表和视图,成为所创建对象的属主。但不能创建模式,不能创建新的用户,可以对型进行改变
拥有DBA权限的用户是系统中的超级用户,可以创建新的用户、创建模式、创建基本表和视图等;DBA拥有对所有数据库对象的存取权限,还可以把这些权限授予一般用户

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f4ayic3c-1687783555087)(null)]

这怎么感觉又有问题?不是RESOURCE权限可以CREATE TABLE吗,这里又给我来个不行,事实是可以的,全当这张表在放屁

数据库角色

数据库角色:被命名的一组与数据库操作相关的权限

  • 角色是权限的集合
  • 可以为一组具有相同权限的用户创建一个角色
  • 简化授权的过程

角色的创建
CREATE ROLE <角色名>
给角色授权
GRANT <权限>[,<权限>]…
ON <对象类型>对象名
TO <角色>[,<角色>]…

角色同权限一样,也是可以传递授予的
将一个角色授予其他的角色或用户
GRANT <角色1>[,<角色2>]…
TO <角色3>[,<用户1>]…
[WITH ADMIN OPTION]

  • 该语句把角色授予某用户,或授予另一个角色
  • 授予者是角色的创建者或拥有在这个角色上的ADMIN OPTION
  • 指定了WITH ADMIN OPTION则获得某种权限的角色或用户还可以把这种权限授予其他角色

一个角色的权限:直接授予这个角色的全部权限加上其他角色授予这个角色的全部权限

角色权限的收回
REVOKE <权限>[,<权限>]…
ON <对象类型> <对象名>
FROM <角色>[,<角色>]…
用户可以回收角色的权限,从而修改角色拥有的权限
REVOKE执行者是

  • 角色的创建者
  • 拥有在这个(些)角色上的ADMIN OPTION
[4.11] 通过角色来实现将一组权限授予一个用户。步骤如下:
首先创建一个角色 R1
    	  CREATE  ROLE  R1;
然后使用GRANT语句,使角色R1拥有Student表的SELECTUPDATEINSERT权限
       	GRANT SELECT, UPDATE, INSERT 
    	 ON TABLE Student 
    	 TO R1;
将这个角色授予王平,张明,赵玲。使他们具有角色R1所包含的全部权限
    	 GRANT  R1 
    	 TO 王平,张明,赵玲;
可以一次性通过R1来回收王平的这3个权限
     	  REVOKE  R1 
     	  FROM 王平;
[4.12] 角色的权限修改
        GRANT DELETE 
        ON TABLE Student
        TO R1;
使角色R1在原来的基础上增加了Student表的DELETE 权限
[4.13] 使R1减少了SELECT权限 
   	 REVOKE SELECT 
        ON TABLE Student
        FROM  R1;

对角色权限修改这些应该也必须考虑级联之类的吧

自主存取控制缺点

可能存在数据的“无意泄露”
原因:这种机制仅仅通过对数据的存取权限来进行安全控制,而数据本身并无安全性标记
解决:对系统控制下的所有主客体实施强制存取控制策略

MAC(强制存取控制)

B1级,保证更高程度的安全性
每一个数据对象被标以一定的密级
每一个用户也被授予某一个级别的许可证
对于任意一个对象,只有具有合法许可证的用户才可以存取
用户不能直接感知或进行控制
适用于对数据有严格而固定密级分类的部门

  • 军事部门
  • 政府部门

在强制存取控制中,数据库管理系统所管理的全部实体被分为主体和客体两大类
主体是系统中的活动实体

  • 数据库管理系统所管理的实际用户
  • 代表用户的各进程

客体是系统中的被动实体,受主体操纵

  • 文件、基本表、索引、视图
强制存取控制方法——敏感度标记

敏感度标记(Label)

  • 对于主体和客体,DBMS为它们每个实例(值)指派一个敏感度标记(Label)
  • 敏感度标记分成若干级别
    • 绝密(Top Secret,TS)
    • 机密(Secret,S)
    • 可信(Confidential,C)
    • 公开(Public,P)
    • TS>=S>=C>=P

主体的敏感度标记称为许可证级别(Clearance Level)
客体的敏感度标记称为密级(Classification Level)

强制存取控制规则

  • 仅当主体的许可证级别大于或等于客体的密级时,该主体才能读取相应的客体
  • 仅当主体的许可证级别小于或等于客体的密级时,该主体才能写相应的客体
  • 同级既可以读也可以写

强制存取控制(MAC)是对数据本身进行密级标记,无论数据如何复制,标记与数据是一个不可分的整体,只有符合密级标记要求的用户才可以操纵数据。

DAC+MAC

实现强制存取控制时要首先实现自主存取控制
原因:较高安全性级别提供的安全保护要包含较低级别的所有保护
自主存取控制与强制存取控制共同构成数据库管理系统的安全机制,先进行自主存取控制检查,通过自主存取控制检查的数据对象再由系统进行强制存取控制检查,只有通过强制存取控制检查的数据对象方可存取。

SQL语法分析、语义检查->DAC检查->MAC检查->继续

视图机制、审计、数据加密及其他

视图机制

把要保密的数据对无权存取这些数据的用户隐藏起来,对数据提供一定程度的安全保护
间接地实现支持存取谓词的用户权限定义

[4.14] 建立计算机系学生的视图,把对该视图的SELECT权限授于王平,把该视图上的所有操作权限授于张明 
先建立计算机系学生的视图CS_Student
CREATE VIEW CS_Student
AS 
SELECT  *
FROM   Student
WHERE  Sdept='CS';
在视图上进一步定义存取权限
     GRANT  SELECT
     ON  CS_Student  
     TO 王平;
  
     GRANT ALL PRIVILIGES
     ON  CS_Student  
     TO  张明;

审计

审计

  • 启用一个专用的审计日志(Audit Log)将用户对数据库的所有操作记录在上面
  • 审计员利用审计日志监控数据库中的各种行为,找出非法存取数据的人、时间和内容
  • C2以上安全级别的DBMS必须具有审计功能

审计功能的可选性

  • 审计很费时间和空间
  • DBA可以根据应用对安全性的要求,灵活地打开或关闭审计功能
  • 审计功能主要用于安全性要求较高的部门
审计事件
  • 服务器事件
    • 审计数据库服务器发生的事件
  • 系统权限
    • 对系统拥有的结构或模式对象进行操作的审计
    • 要求该操作的权限是通过系统权限获得的
  • 语句事件
    • 对SQL语句,如DDL、DML、DQL及DCL语句的审计
  • 模式对象事件
    • 对特定模式对象上进行的SELECT或DML操作的审计
审计功能
  • 基本功能
    • 提供多种审计查阅方式
  • 多套审计规则:一般在初始化设定
    • 提供审计分析和报表功能
  • 审计日志管理功能
    • 防止审计员误删审计记录,审计日志必须先转储后删除
    • 对转储的审计记录文件提供完整性和保密性保护
    • 只允许审计员查阅和转储审计记录,不允许任何用户新增和修改审计记录等
  • 提供查询审计设置及审计记录信息的专门视图
审计级别

用户级审计

  • 任何用户可设置的审计
  • 主要是用户针对自己创建的数据库表和视图进行审计

系统级审计

  • 只能由数据库管理员设置
  • 监测成功或失败的登录要求、监测授权和收回操作以及其他数据库级权限下的操作
审计语句

AUDIT语句和NOAUDIT语句

  • AUDIT语句:设置审计功能
  • NOAUDIT语句:取消审计功能
[4.15] 对修改SC表结构或修改SC表数据的操作进行审计
AUDIT ALTER,UPDATE  
ON  SC;
[4.16] 取消对SC表的一切审计
NOAUDIT  ALTER,UPDATE  
ON  SC;

加密

数据加密
  • 数据加密
    • 防止数据库中数据在存储和传输中失密的有效手段
  • 加密的基本思想
    • 根据一定的算法将原始数据—明文(Plain text)变换为不可直接识别的格式­—密文(Cipher text)
  • 加密方法
    • 存储加密
    • 传输加密
存储加密
  • 透明存储加密
    • 内核级加密保护方式,对用户完全透明
    • 将数据在写到磁盘时对数据进行加密,授权用户读取数据时再对其进行解密
    • 数据库的应用程序不需要做任何修改,只需在创建表语句中说明需加密的字段即可
    • 内核级加密方法: 性能较好,安全完备性较高
  • 非透明存储加密
    • 通过多个加密函数实现
传输加密
  • 链路加密
    • 在链路层进行加密
    • 传输信息由报头和报文两部分组成
    • 报文和报头均加密
  • 端到端加密
    • 在发送端加密,接收端解密
    • 只加密报文不加密报头
    • 所需密码设备数量相对较少,容易被非法监听者发现并从中获取敏感信息

其他安全保护

推理控制

  • 避免用户利用能够访问的数据推知更高密级的数据

隐蔽信道

  • 间接数据传递

数据隐私保护

  • 描述个人控制其不愿他人知道或他人不便知道的个人数据的能力
  • 范围很广:数据收集、数据存储、数据处理和数据发布等各个阶段

Part5——数据库完整性

数据库完整性

数据库的完整性

  • 数据的正确性
    • 是指数据是符合现实世界语义,反映了当前实际状况的
  • 数据的相容性
    • 是指数据库同一对象在不同关系表中的数据是符合逻辑的

例如,
学生的学号必须唯一
性别只能是男或女
本科学生年龄的取值范围为14~50的整数
学生所选的课程必须是学校开设的课程,学生所在的院系必须是学校已成立的院系
……

完整性 vs.安全性

数据的完整性和安全性是两个不同概念

  • 数据的完整性
    • 防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据
    • 防范对象:不合语义的、不正确的数据
  • 数据的安全性
    • 保护数据库,防止恶意的破坏和非法的存取
    • 防范对象:非法用户和非法操作

完整性机制

  • 提供定义完整性约束条件的机制
    • 完整性约束条件也称为完整性规则,是数据库中的数据必须满足的语义约束条件
    • SQL标准使用了一系列概念来描述完整性,包括关系模型的实体完整性参照完整性用户定义完整性
    • 这些完整性一般由SQL的数据定义语言语句来实现
  • 提供完整性检查的方法
    • 数据库管理系统中检查数据是否满足完整性约束条件的机制称为完整性检查。
    • 一般在INSERT、UPDATE、DELETE语句执行后开始检查,也可以在事务提交时检查
  • 违约处理
    • 数据库管理系统若发现用户的操作违背了完整性约束条件,就采取一定的动作
      • 拒绝(NO ACTION)执行该操作
      • 级连(CASCADE)执行其他操作

实体完整性

实体完整性定义

关系模型的实体完整性

  • CREATE TABLE中用PRIMARY KEY定义

单属性构成的码有两种说明方法

  • 定义为列级约束条件
  • 定义为表级约束条件

对多个属性构成的码只有一种说明方法

  • 定义为表级约束条件
[5.1] 将Student表中的Sno属性定义为码
在列级定义主码              
CREATE TABLE Student
   (  Sno  CHAR(9)  PRIMARY KEY,
      Sname  CHAR(20) NOT NULL,     
      Ssex  CHAR(2),
      Sage  SMALLINT,
      Sdept  CHAR(20)
   );
在表级定义主码
CREATE TABLE Student
        (  Sno  CHAR(9),  
           Sname  CHAR(20) 			NOT NULL,
           Ssex  CHAR(2),
           Sage  SMALLINT,
           Sdept  CHAR(20),
           PRIMARY KEY (Sno)
         );
[5.2] 将SC表中的Sno,Cno属性组定义为码
      CREATE TABLE SC
           (  Sno   CHAR(9)  NOT NULL, 
              Cno  CHAR(4)  NOT NULL,  
              Grade    SMALLINT,
              PRIMARY KEY (Sno,Cno)    /*只能在表级定义主码*/
           ); 

实体完整性检查和违约处理

插入或对主码列进行更新操作时,关系数据库管理系统按照实体完整性规则自动进行检查。包括:

  • 检查主码值是否唯一,如果不唯一则拒绝插入或修改
  • 检查主码的各个属性是否为空,只要有一个为空就拒绝插入或修改

全表扫描——十分费时
自动建立索引——快速

参照完整性

参照完整性定义

关系模型的参照完整性定义
在CREATE TABLE中用FOREIGN KEY短语定义哪些列为外码
用REFERENCES短语指明这些外码参照哪些表的主码

[5.3]定义SC中的参照完整性
         CREATE TABLE SC
         (  Sno    CHAR(9)  NOT NULL, 
            Cno     CHAR(4)  NOT NULL,  
            Grade    SMALLINT,
            PRIMARY KEY (Sno, Cno),   /*在表级定义实体完整性*/
               FOREIGN KEY (Sno) REFERENCES Student(Sno),  
                  /*在表级定义参照完整性*/
               FOREIGN KEY (Cno) REFERENCES Course(Cno)    
                  /*在表级定义参照完整性*/
             );

参照完整性检查

一个参照完整性将两个表中的相应元组联系起来
对被参照表和参照表进行增删改操作时有可能破坏参照完整性,必须进行检查
例如,对表SC和Student有四种可能破坏参照完整性的情况 :
SC表中增加一个元组,该元组的Sno属性的值在表Student中找不到一个元组,其Sno属性的值与之相等。
修改SC表中的一个元组,修改后该元组的Sno属性的值在表Student中找不到一个元组,其Sno属性的值与之相等。
从Student表中删除一个元组,造成SC表中某些元组的Sno属性的值在表Student中找不到一个元组,其Sno属性的值与之相等。
修改Student表中一个元组的Sno属性,造成SC表中某些元组的Sno属性的值在表Student中找不到一个元组,其Sno属性的值与之相等 。

违约处理

参照完整性违约处理

  • 拒绝(NO ACTION)执行
    • 不允许该操作执行。该策略一般设置为默认策略
  • 级联(CASCADE)操作
    • 当删除或修改被参照表(Student)的一个元组造成了与参照表(SC)的不一致,则删除或修改参照表中的所有造成不一致的元组
  • 设置为空值(SET-NULL)
    • 当删除或修改被参照表的一个元组时造成了不一致,则将参照表中的所有造成不一致的元组的对应属性设置为空值。
    • 对于参照完整性,除了应该定义外码,还应定义外码列是否允许空值
[5.4]  显式说明参照完整性的违约处理示例
       CREATE TABLE SC
        (  Sno   CHAR(9)  NOT NULL,
           Cno   CHAR(4)  NOT NULL,
           Grade  SMALLINT,
           PRIMARY KEY(Sno,Cno)FOREIGN KEY (Sno) REFERENCES Student(Sno) 
	ON DELETE CASCADE        /*级联删除SC表中相应的元组*/
             	ON UPDATE CASCADE,      /*级联更新SC表中相应的元组*/
           FOREIGN KEY (Cno) REFERENCES Course(Cno)	                    
             	ON DELETE NO ACTION 	
                 /*当删除course 表中的元组造成了与SC表不一致时拒绝删除*/
             	ON UPDATE CASCADE   
      	  /*当更新course表中的cno时,级联更新SC表中相应的元组*/
           );

用户定义的完整性

用户定义完整性定义

用户定义的完整性是:针对某一具体应用的数据必须满足的语义要求

  • 属性上的约束条件//值域、规范
  • 元组上的约束条件
    • 同属性值限制相比,元组级的限制可以设置不同属性之间的取值的相互约束条件

关系数据库管理系统提供了定义和检验用户定义完整性的机制,不必由应用程序承担

  • 插入元组或修改属性的值时,关系数据库管理系统检查约束条件是否被满足
  • 如果不满足则操作被拒绝执行
  • //共性检查/特性处理

列值非空

  [5.5]  在定义SC表时,说明Sno、Cno、Grade属性不允许取空值。
        CREATE TABLE SC
        (  Sno CHAR(9)  NOT NULL,	
           Cno CHAR(4)  NOT NULL,	
           Grade  SMALLINT NOT NULL,	
           PRIMARY KEY (Sno, Cno),/* 如果在表级定义实体完整性,隐含了Sno,Cno不允许取空值,则在  
             列级不允许取空值的定义 可以不写 * /
         ); 

列值唯一

[5.6]建立部门表DEPT,要求部门名称Dname列取值唯一,部门编号Deptno列为主码
    CREATE TABLE DEPT
        (   Deptno  NUMERIC(2),
            Dname  CHAR(9)  UNIQUE NOT NULL/*要求Dname列值唯一, 并且不能取空值*/
//一般情况unique就是not null
            Location  CHAR(10),
            PRIMARY KEY (Deptno)
         );

列值满足条件

用CHECK短语指定列值应该满足的条件

[5.7]  Student表的Ssex只允许取“男”或“女”。
     CREATE TABLE Student
          ( Sno  CHAR(9) PRIMARY KEY,
            Sname CHAR(8) NOT NULL,                     
            Ssex  CHAR(2)  CHECK (Ssex IN (‘男’,’女’)),           
                                                     /*性别属性Ssex只允许取'男'或'女' */
            Sage  SMALLINT,
            Sdept  CHAR(20)
          );
[5.8]  SC表的Grade的值应该在0100之间。
   CREATE TABLE  SC
         (  Sno     CHAR(9) ,
            Cno    CHAR(4),
	Grade   SMALLINT CHECK (Grade>=0 AND Grade <=100)/*Grade取值范围是0到100*/
            PRIMARY KEY (Sno,Cno),
            FOREIGN KEY (Sno) REFERENCES Student(Sno),
            FOREIGN KEY (Cno) REFERENCES Course(Cno)
           );

元组约束条件

[5.9]当学生的性别是男时,其名字不能以Ms.打头。
    CREATE TABLE Student
         (  Sno    CHAR(9), 
            Sname  CHAR(8) NOT NULL,
            Ssex    CHAR(2),
            Sage   SMALLINT,
            Sdept  CHAR(20),
            PRIMARY KEY (Sno),
            CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
                   /*定义了元组中Sname和 Ssex两个属性值之间的约束条件*/
          );
性别是女性的元组都能通过该项检查,因为Ssex=‘女’成立;
当性别是男性时,要通过检查则名字一定不能以Ms.打头

完整性约束命名

创建完整性约束:
完整性约束命名子句
CONSTRAINT <完整性约束条件名><完整性约束条件>
<完整性约束条件>包括NOT NULL、UNIQUE、PRIMARY KEY短语、FOREIGN KEY短语、CHECK短语等

修改完整性约束:
使用ALTER TABLE语句修改表中的完整性限制

创建完整性约束
[5.10]建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”。
    CREATE TABLE Student
      (   Sno  NUMERIC(6)
          CONSTRAINT C1 CHECK (Sno BETWEEN 90000 AND 99999),
          Sname  CHAR(20)  
          CONSTRAINT C2 NOT NULL,
          Sage  NUMERIC(3)
          CONSTRAINT C3 CHECK (Sage < 30),
          Ssex  CHAR(2)
          CONSTRAINT C4 CHECK (Ssex IN ( ‘男’,'女')),
          CONSTRAINT StudentKey PRIMARY KEY(Sno)
        );
在Student表上建立了5个约束条件,包括主码约束(命名为StudentKey)以及C1、C2、C3、C4四个列级约束。
[5.11]建立教师表TEACHER,要求每个教师的应发工资不低于3000元。应发工资是工资列Sal与扣除项Deduct之和。
      CREATE TABLE TEACHER
              (   Eno    NUMERIC(4)  PRIMARY KEY    /*在列级定义主码*/
                  Ename  CHAR(10),
                  Job     CHAR(8),
 		      Sal     NUMERIC(7,2),
 		      Deduct  NUMERIC(7,2),
 		      Deptno  NUMERIC(2),
  		      CONSTRAINT TEACHERFKey FOREIGN KEY (Deptno) REFERENCES DEPT(Deptno),
  		      CONSTRAINT C1 CHECK (Sal + Deduct >= 3000) 
                );

修改完整性约束
[5.12]去掉例5.10 Student表中对性别的限制。
        ALTER TABLE Student 
        DROP CONSTRAINT C4;
 [5.13]  修改表Student中的约束条件,要求学号改为在900000~999999之间,年龄由小于30改为小于40
可以先删除原来的约束条件,再增加新的约束条件
        ALTER TABLE Student
        DROP CONSTRAINT C1;
        ALTER TABLE Student
        ADD CONSTRAINT C1 CHECK (Sno BETWEEN 900000 AND 999999),
        ALTER TABLE Student
        DROP CONSTRAINT C3;
        ALTER TABLE Student
        ADD CONSTRAINT C3 CHECK(Sage < 40);

断言

SQL中,可以使用 CREATE ASSERTION语句,通过声明性断言来指定更具一般性的约束。
可以定义涉及多个表的或聚集操作的比较复杂的完整性约束。
断言创建以后,任何对断言中所涉及的关系的操作都会触发关系数据库管理系统对断言的检查,任何使断言不为真值的操作都会被拒绝执行
如果断言很复杂,则系统在检测和维护断言的开销较高,这是在使用断言时应该注意的

创建断言的语句格式
CREATE ASSERTION<断言名><CHECK 子句>
每个断言都被赋予一个名字,<CHECK 子句>中的约束条件与WHERE子句的条件表达式类似。
删除断言的语句格式为
DROP ASSERTION <断言名>;

[5.18] 限制数据库课程最多60名学生选修
	CREATE ASSERTION ASSE_SC_DB_NUM
	CHECK (60 >= (select count(*)
/*此断言的谓词涉及聚集操作count的SQL语句*/
 	From Course,SC
    	Where SC.Cno=Course.Cno and 						Course.Cname ='数据库')
		   	);
[5.19]限制每一门课程最多60名学生选修
	CREATE ASSERTION ASSE_SC_CNUM1
	CHECK(60 >= ALL (SELECT count(*) 					          
                        FROM	 SC 
				        GROUP by cno)
        		  );
 /*此断言的谓词,涉及聚集操作count 和分组函数group by的SQL语句*/
[5.20]限制每个学期每一门课程最多60名学生选修。首先需要修改SC表的模式,增加一个“学期(TERM)”属性
	 ALTER TABLE SC ADD TERM DATE;
然后,定义断言:
    	 CREATE ASSERTION ASSE_SC_CNUM2
 	 CHECK(60 >= ALL (SELECT count(*)                         		
                FROM SC
				GROUP by cno,TERM)
				);

触发器

触发器(Trigger)是用户定义在关系表上的一类由事件驱动的特殊过程

  • 触发器保存在数据库服务器中
  • 任何用户对表的增、删、改操作均由服务器自动激活相应的触发器
  • 触发器可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制能力

不同的RDBMS产品触发器语法各不相同

定义触发器

CREATE TRIGGER语法格式
CREATE TRIGGER <触发器名>
{BEFORE | AFTER} <触发事件> ON <表名>
REFERENCING NEW|OLD ROW AS<变量>
FOR EACH {ROW | STATEMENT}
[WHEN <触发条件>]<触发动作体>
触发器又叫做事件-条件-动作(event-condition-action)规则。
当特定的系统事件发生时,对规则的条件进行检查,如果条件成立则执行规则中的动作,否则不执行该动作。规则中的动作体可以很复杂,通常是一段SQL存储过程。

表的拥有者才可以在表上创建触发器

  • 触发器名
    • 触发器名可以包含模式名,也可以不包含模式名
    • 同一模式下,触发器名必须是唯一的
    • 触发器名和表名必须在同一模式下
  • 表名
    • 触发器只能定义在基本表上,不能定义在视图上
    • 当基本表的数据发生变化时,将激活定义在该表上相应触发事件的触发器
  • 触发事件
    • 触发事件可以是INSERT、DELETE或UPDATE,也可以是这几个事件的组合
    • 还可以UPDATE OF<触发列,…>,即进一步指明修改哪些列时激活触发器,inserted和delete都没有
    • AFTER/BEFORE是触发的时机
      • AFTER表示在触发事件的操作执行之后激活触发器
      • BEFORE表示在触发事件的操作执行之前激活触发器
  • 触发器类型
    • 行级触发器(FOR EACH ROW)
    • 语句级触发器(FOR EACH STATEMENT)
  • 触发条件
    • 触发器被激活时,只有当触发条件为真时触发动作体才执行;否则触发动作体不执行。
    • 如果省略WHEN触发条件,则触发动作体在触发器激活后立即执行
  • 触发动作体
    • 触发动作体可以是一个匿名PL/SQL过程块,也可以是对已创建存储过程的调用
    • 如果是行级触发器,用户可以在过程体中使用NEW和OLD引用事件之后的新值和事件之前的旧值
    • 如果是语句级触发器,则不能在触发动作体中使用NEW或OLD进行引用
    • 如果触发动作体执行失败,激活触发器的事件就会终止执行,触发器的目标表或触发器可能影响的其他对象不发生任何变化

触发器两种类型:
例如,在例5.11的TEACHER表上创建一个AFTER UPDATE触发器,触发事件是UPDATE语句:
UPDATE TEACHER SET Deptno=5;
假设表TEACHER有1000行
如果是语句级触发器,那么执行完该语句后,触发动作只发生一次
如果是行级触发器,触发动作将执行1000次

[5.21]当对表SC的Grade属性进行修改时,若分数增加了10%则将此次操作记录到下面表中:SC_U(Sno,Cno,Oldgrade,Newgrade)其中Oldgrade是修改前的分数,Newgrade是修改后的分数。
	CREATE TRIGGER  SC_T		
	AFTER UPDATE OF Grade ON SC
     	REFERENCING
	      OLD row  AS  OldTuple,
	      NEW row AS  NewTuple
	FOR EACH ROW 	
	WHEN (NewTuple.Grade >= 1.1*OldTuple.Grade)
	    INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade)  
VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
[5.22] 将每次对表Student的插入操作所增加的学生个数记录到表StudentInsertLog中。
	CREATE TRIGGER Student_Count
	AFTER INSERT ON Student  
	        /*指明触发器激活的时间是在执行INSERT后*/     
	REFERENCING
     	NEW TABLE AS DELTA
	FOR EACH STATEMENT  
/*语句级触发器, 即执行完INSERT语句后下面的触发动作体才执行一次*/
		INSERT INTO StudentInsertLog (Numbers)
   		SELECT COUNT(*) FROM DELTA
[5.23] 定义一个BEFORE行级触发器,为教师表Teacher定义完整性规则“教授的工资不得低于4000元,如果低于4000元,自动改为4000元”。
        CREATE TRIGGER Insert_Or_Update_Sal 
         BEFORE INSERT OR UPDATE ON Teacher  
         			             /*触发事件是插入或更新操作*/
          REFERENCING NEW row AS newTuple
          FOR EACH ROW        /*行级触发器*/
          BEGIN                             /*定义触发动作体,是PL/SQL过程块*/
             		 IF (newTuple.Job='教授') AND (newTuple.Sal < 4000) 
             	 THEN newTuple.Sal :=4000;                
           	 END IF;
        END;   
-- 由触发器语句的副作用我们对教授工资低于4000时进行了更改

激活触发器

触发器的执行,是由触发事件激活的,并由数据库服务器自动执行
一个数据表上可能定义了多个触发器,遵循如下的执行顺序:

  • 执行该表上的BEFORE触发器;
  • 激活触发器的SQL语句;
  • 执行该表上的AFTER触发器。

删除激活器

删除触发器的SQL语法:
DROP TRIGGER <触发器名> ON <表名>;
触发器必须是一个已经创建的触发器,并且只能由具有相应权限的用户删除。

Part6——范式

重点章节,虽然我之前就没太听的懂,希望这次能有深刻理解

关系模式和范式

关系模式及第一范式(1NF)

此前章节对于关系模式已经进行了多次描述,不加以赘述,简而言之,关系模式是型
关系模式由五部分组成,是一个五元组:
R(U, D, DOM, F)
关系名R是符号化的元组语义
U为一组属性
D为属性组U中的属性所来自的域
DOM为属性到域的映射
F为属性组U上的一组数据依赖

由于D、DOM与模式设计关系不大,因此可以把关系模式看作一个三元组:R<U,F>
当且仅当U上的一个关系r满足F时,r称为关系模式R<U,F>的一个关系
作为二维表,关系要符合一个最基本的条件:每个分量必须是不可分开的数据项。满足了这个条件的关系模式就属于第一范式(1NF)

数据依赖

数据依赖

  • 是一个关系内部属性与属性之间的一种约束关系
    • 通过属性间值的相等与否体现出来的数据间相关联系
  • 是现实世界属性间相互联系的抽象
  • 是数据内在的性质
  • 是语义的体现

数据依赖的主要类型

  • 函数依赖(Functional Dependency,简记为FD)
  • 多值依赖(Multi-Valued Dependency,简记为MVD)
函数依赖

函数依赖普遍存在于现实生活中
描述一个学生关系,可以有学号、姓名、系名等属性。
一个学号只对应一个学生,一个学生只在一个系中学习
“学号”值确定后,学生的姓名及所在系的值就被唯一确定。
Sname=f(Sno),Sdept=f(Sno)
即Sno函数决定Sname,Sno函数决定Sdept
记作Sno→Sname,Sno→Sdept

1NF

[例6.1] 建立一个描述学校教务的数据库。涉及的对象包括:
学生的学号(Sno)
所在系(Sdept)
系主任姓名(Mname)
课程号(Cno)
成绩(Grade)

假设学校教务的数据库模式用一个单一的关系模式Student来表示,则该关系模式的属性集合为:
U ={Sno, Sdept, Mname, Cno, Grade}
现实世界的已知事实(语义):
一个系有若干学生, 但一个学生只属于一个系;
一个系只有一名(正职)负责人;
一个学生可以选修多门课程,每门课程有若干学生选修;
每个学生学习每一门课程有一个成绩。
由此可得到属性组U上的一组函数依赖F:
F={Sno→Sdept, Sdept→ Mname, (Sno, Cno)→ Grade}

1NF的问题

关系模式Student<U, F>中存在的问题:

  • 数据冗余
    • 浪费大量的存储空间
      • 每一个系主任的姓名重复出现,重复次数与该系所有学生的所有课程成绩出现次数相同。
  • 修改复杂,更新异常(Update Anomalies)
    • 数据冗余 ,更新数据时,维护数据完整性代价大。
      • 某系更换系主任后,必须修改与该系学生有关的每一个元组。
  • 插入异常(Insertion Anomalies)
    • 如果一个系刚成立,尚无学生,则无法把这个系及其系主任的信息存入数据库。
  • 删除异常(Deletion Anomalies)
    • 如果某个系的学生全部毕业了, 则在删除该系学生信息的同时,把这个系及其系主任的信息也丢掉了。

结论

  • Student关系模式不是一个好的模式。
  • 一个“好”的模式应当不会发生插入异常、删除异常和更新异常,数据冗余应尽可能少。

原因

  • 由存在于模式中的某些数据依赖引起的。

解决方法

  • 用规范化理论改造关系模式来消除其中不合适的数据依赖

把这个单一的模式分成三个关系模式:
S(Sno,Sdept,Sno → Sdept);
SC(Sno,Cno,Grade,(Sno,Cno) → Grade);
DEPT(Sdept,Mname,Sdept → Mname);
这三个模式都不会发生插入异常、删除异常的问题,数据的冗余也得到了控制。

所以也没有解释1NF是啥,不过后面章节会有详细描述,就是这阅读体验挺跳跃…

范式

范式是符合某一种级别的关系模式的集合。
关系数据库中的关系必须满足一定的要求。满足不同程度要求的为不同范式。
范式的种类:

  • 第一范式(1NF)
  • 第二范式(2NF)
  • 第三范式(3NF)
  • BC范式(BCNF)
  • 第四范式(4NF)
  • 第五范式(5NF)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOQ1Wbs2-1687783560955)(null)]

模式分解?

函数依赖与码

函数依赖

定义6.1 设R(U)是一个属性集U上的关系模式,X和Y是U的子集。若对于R(U)的任意一个可能的关系r,r 中不可能存在两个元组在X上的属性值相等, 而在Y上的属性值不等, 则称“X函数确定Y”或“Y函数依赖于X”,记作X→Y,X称为这个函数依赖的决定因素(Determinant)。

若X→Y,并且Y→X, 则记为X←→Y。

函数依赖不是指关系模式R的某个或某些关系实例满足的约束条件,而是指R的所有关系实例均要满足的约束条件。
函数依赖是语义范畴的概念。只能根据数据的语义来确定函数依赖。
例如“姓名→年龄”这个函数依赖只有在不允许有同名人的条件下成立
数据库设计者可以对现实世界作强制的规定。
例如规定不允许同名人出现,函数依赖“姓名→年龄”成立。所插入的元组必须满足规定的函数依赖,若发现有同名人存在,则拒绝插入该元组。

非平凡/平凡函数依赖

在关系模式R(U)中,对于U的子集X和Y,
X→Y,但Y ⊈ X,则称X→Y是非平凡的函数依赖
X→Y,但Y包含于X,则称X→Y是平凡的函数依赖
对于任一关系模式,平凡函数依赖都是必然成立的,它不反映新的语义。因此若不特别声明, 我们总是讨论非平凡函数依赖。
例:在关系SC(Sno, Cno, Grade)中,
非平凡函数依赖: (Sno, Cno) → Grade
平凡函数依赖: (Sno, Cno) → Sno
(Sno, Cno) → Cno

完全/部分函数依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lvq6TEb1-1687783558502)(null)]

传递函数依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ITazcFxy-1687783557539)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AW1Fz2E9-1687783556095)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QR8jgWvA-1687783559255)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwXJGEwe-1687783550251)(null)]

范式

1NF

如果一个关系模式R的所有属性都是不可分的基本数据项,属于第一范式关系的所有属性都不可再分,即数据项不可分,则R∈1NF。
第一范式是对关系模式的最起码的要求。不满足第一范式的数据库模式不能称为关系数据库。
但是满足第一范式的关系模式并不一定是一个好的关系模式。

2NF

定义6.6 若关系模式R∈1NF,并且每一个非主属性都完全函数依赖于任何一个候选码,则R∈2NF
[例6.4] S-L-C(Sno,Sdept,Sloc,Cno,Grade), Sloc为学生的住处,并且每个系的学生住在同一个地方。S-L-C的码为(Sno,Cno)。
函数依赖有
(Sno,Cno)→Grade
Sno→Sdept, (Sno,Cno)→Sdept
Sno→Sloc, (Sno,Cno)→Sloc
Sdept→Sloc
非主属性Sdept和Sloc部分函数依赖于码(Sno, Cno)
关系模式S-L-C不属于2NF

判断一个关系是否属于第二范式:
找出数据表中的所有码;
找出所有主属性和非主属性;
判断所有的非主属性对码的部分函数依赖。

一个关系模式不属于2NF,会产生以下问题:

  • 数据冗余
  • 修改复杂,更新异常
    • 如果一个学生选了多门课,则Sdept,Sloc被存储了多次。如果该生转系,则需要修改所有相关的Sdept和Sloc,造成修改的复杂化
  • 插入异常
    • 如果插入一个新学生,但该生未选课,即该生无Cno,由于插入元组时,必须给定码值,因此插入失败。
  • 删除异常
    • 如果S4只选了一门课C3,现在他不再选这门课,则删除C3后,整个元组的其他信息也被删除了。

出现这种问题的原因
例子中有两类非主属性:
一类如Grade,它对码完全函数依赖
另一类如Sdept、Sloc,它们对码不是完全函数依赖
解决方法:
用投影分解把关系模式S-L-C分解成两个关系模式
SC(Sno,Cno,Grade)
S-L(Sno,Sdept,Sloc)

问题(以学生为例)
数据冗余
有改进
修改复杂,更新异常
有改进,如,修改学生院系,只需修改SL表格
插入异常
仍然存在
删除异常
仍然存在

3NF

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvX6sdIc-1687783550902)(null)]

只是没有传递依赖就行了?

问题(以学生为例)
数据冗余
有改进
修改复杂,更新异常
插入异常
有改进
删除异常
有改进

说实话真没看出来这些异常改进在哪,还有3NF应该是在2NF基础上,第三范式要求在满足第二范式的基础上,任何非主属性不依赖于其他非主属性,即在第二范式的基础上,消除了传递依赖。

BCNF

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DniQA9PA-1687783554295)(null)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvIzFY1h-1687783553142)(null)]

定义: 关系模式R<U,F>中,若每一个决定因素都包含码,则R<U,F>属于BCFN。

BCNF的关系模式所具有的性质

  • 所有非主属性都完全函数依赖于每个候选码
  • 所有主属性都完全函数依赖于每个不包含它的候选码
  • 没有任何属性完全函数依赖于非码的任何一组属性

如果一个关系数据库中的所有关系模式都属于BCNF,那么在函数依赖范畴内,它已实现了模式的彻底分解,达到了最高的规范化程度,消除了插入异常和删除异常。

[例6.5]考察关系模式C(Cno,Cname,Pcno)
它只有一个码Cno,没有任何属性对Cno部分依赖或传递依赖,所以C∈3NF。
同时C中Cno是唯一的决定因素,所以C∈BCNF。
对于关系模式SC(Sno,Cno,Grade)可作同样分析。
[例6.6] 关系模式S(Sno,Sname,Sdept,Sage),
假定Sname也具有唯一性,那么S就有两个码,这两个码都由单个属性组成,彼此不相交。
其他属性不存在对码的传递依赖与部分依赖,所以S∈3NF。
同时S中除Sno,Sname外没有其他决定因素,所以S也属于BCNF。

[例6.7] 关系模式SJP(S,J,P)中,S是学生,J表示 课程,P表示名次。每一个学生选修每门课程的成绩有一定的名次,每门课程中每一名次只有一个学生(即没有并列名次)。

  • 由语义可得到函数依赖: (S,J)→P;(J,P)→S
  • (S,J)与(J,P)都可以作为候选码。
  • 关系模式中没有属性对码传递依赖或部分依赖,所以SJP∈3NF。
  • 除(S,J)与(J,P)以外没有其他决定因素,所以SJP∈BCNF。

[例6.8] 关系模式STJ(S,T,J)中,S表示学生,T表示教师,J表示课程。每一教师只教一门课。每门课有若干教师,某一学生选定某门课,就对应一个固定的教师。

  • 由语义可得到函数依赖:(S,J)→T;(S,T)→J;T→J
  • 因为没有任何非主属性对码传递依赖或部分依赖,STJ ∈ 3NF。
  • 因为T是决定因素,而T不包含码,所以STJ ∉ BCNF关系。

非BCNF的关系模式也可以通过分解成为BCNF。例如STJ可分解为ST(S,T)与TJ(T,J),它们都是BCNF。
3NF和BCNF是在函数依赖的条件下对模式分解所能达到的分离程度的测度。
一个模式中的关系模式如果都属于BCNF,那么在函数依赖范畴内,它已实现了彻底的分离,已消除了插入和删除的异常。
3NF的“不彻底”性表现在可能存在主属性对码的部分依赖和传递依赖。

多值依赖与4NF

例[6.9]设学校中某一门课程由多个教师讲授,他们使用相同的一套参考书。每个教员可以讲授多门课程,每种参考书可以供多门课程使用
用关系模式Teaching(C,T,B)来表示课程C、教师T和参考书B之间的关系。
Teaching具有唯一候选码(C,T,B), 即全码。
Teaching∈BCNF

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qIn88SIu-1687783552218)(null)]

多值依赖的另一个等价的定义:
在R(U)的任一关系r中,如果存在元组t,s使得t[X]=s[X],那么就必然存在元组w,v∈r,(w,v可以与s,t相同), 使得w[X]=v[X]=t[X],而w[Y]=t[Y],w[Z]=s[Z],v[Y]=s[Y],v[Z]=t[Z](即交换s,t元组的Y值所得的两个新元组必在r中,则Y多值依赖于X,记为X→→Y。这里X,Y是U的子集,Z=U-X-Y。

这等价怎么给我看迷糊了,什么玩意?

多值依赖的性质:
对称性、传递性

函数依赖是多值依赖的特殊情况

看不下去了,太绕了,鉴于bj说了4NF不考,暂且将这里搁置,只是对多值依赖概念有了了解。

Part7——数据库设计

也是看个乐就行,全是概念,看个屁

数据库设计概述

数据库设计是指对于一个给定的应用环境,构造(设计)优化的数据库逻辑模式和物理结构,并据此建立数据库及其应用系统,使之能够有效地存储和管理数据,满足各种用户的应用需求,包括信息管理要求和数据操作要求。
信息管理要求:在数据库中应该存储和管理哪些数据对象。
数据操作要求:对数据对象需要进行哪些操作,如查询、增、删、改、统计等操作。

数据库设计的目标是为用户和各种应用系统提供一个信息基础设施和高效率的运行环境 。
高效率的运行环境
数据库数据的存取效率高
数据库存储空间的利用率高
数据库系统运行管理的效率高

数据库设计特点:
数据库建设的基本规律

  • 三分技术,七分管理,十二分基础数据

管理

  • 数据库建设项目管理
  • 企业(即应用部门)的业务管理

基础数据

  • 数据的收集、整理、组织和不断更新

结构(数据)设计和行为(处理)设计相结合

  • 将数据库结构设计和数据处理设计密切结合
  • 结构和行为分离的设计

数据库设计方法:
大型数据库设计是涉及多学科的综合性技术,又是一项庞大的工程项目。
它要求多方面的知识和技术。
手工试凑法
规范设计法

数据库设计基本步骤

数据库设计分6个阶段

  • 需求分析
  • 概念结构设计
  • 逻辑结构设计
  • 物理结构设计
  • 数据库实施
  • 数据库运行和维护
    需求分析和概念设计独立于任何数据库管理系统
    逻辑设计和物理设计与选用的数据库管理系统密切相关

接下来几章节也是按照数据库设计的这几个步骤来讲

设计一个完善的数据库应用系统 往往是上述6个阶段的不断反复
这个设计步骤既是数据库设计的过程,也包括了数据库应用系统的设计过程
把数据库的设计和对数据库中数据处理的设计紧密结合起来,将这两个方面的需求分析、抽象、设计、实现在各个阶段同时进行,相互参照,相互补充,以完善两方面的设计

需求分析

软工二需求课,你也给我来这

需求分析就是分析用户的要求

  • 是设计数据库的起点
  • 结果是否准确地反映了用户的实际要求,将直接影响到后面各个阶段的设计,并影响到设计结果是否合理和实用

详细调查现实世界要处理的对象(组织、部门、企业等)
充分了解原系统(手工系统或计算机系统)工作概况
明确用户的各种需求
在此基础上确定新系统的功能
新系统必须充分考虑今后可能的扩充和改变

调查的重点是“数据”和“处理”,获得用户对数据库的要求

  • 信息要求
    • 用户需要从数据库中获得信息的内容与性质
    • 由信息要求可以导出数据要求,即在数据库中需要存储哪些数据
  • 处理要求
    • 用户要完成的处理功能
    • 对处理性能的要求
  • 安全性与完整性要求

确定用户最终需求的难点
用户缺少计算机知识,不能准确地表达自己的需求,他们所提出的需求往往不断地变化。
设计人员缺少用户的专业知识,不易理解用户的真正需求,甚至误解用户的需求
解决方法
设计人员必须不断深入地与用户进行交流,才能逐步确定用户的实际需求

分析方法:

  • 结构化分析方法(Structured Analysis,简称SA方法)
    • SA方法从最上层的系统组织机构入手
    • 采用自顶向下、逐层分解的方式分析系统

数据字典

数据字典是关于数据库中数据的描述,即元数据,不是数据本身
数据字典在需求分析阶段建立,在数据库设计过程中不断修改、充实、完善
数据字典是进行详细的数据收集和数据分析所获得的主要结果
注意:
和关系数据库管理系统中数据字典的区别和联系

数据字典的内容
数据项
数据结构
数据流
数据存储
处理过程

数据项是数据的最小组成单位
若干个数据项可以组成一个数据结构
数据字典通过对数据项和数据结构的定义来描述数据流、数据存储的逻辑内容

数据项

数据项是不可再分的数据单位
对数据项的描述
数据项描述={数据项名,数据项含义说明,别名,
数据类型,长度,取值范围,取值含义,
与其他数据项的逻辑关系,
数据项之间的联系}
“取值范围”、“与其他数据项的逻辑关系”定义了数据的完整性约束条件,是设计数据检验功能的依据
可以用关系规范化理论为指导,用数据依赖的概念分析和表示数据项之间的联系

数据结构

数据结构反映了数据之间的组合关系。
一个数据结构可以由若干个数据项组成,也可以由若干个数据结构组成,或由若干个数据项和数据结构混合组成。
对数据结构的描述
数据结构描述=
{数据结构名,含义说明,组成:{数据项或数据结构}}

数据流

数据流是数据结构在系统内传输的路径。
对数据流的描述
  数据流描述={数据流名,说明,数据流来源,
数据流去向,组成:{数据结构},
平均流量,高峰期流量}
数据流来源:说明该数据流来自哪个过程
数据流去向:说明该数据流将到哪个过程去
平均流量:在单位时间(每天、每周、每月等)里的传输次数
高峰期流量:在高峰时期的数据流量

数据存储

数据存储是数据结构停留或保存的地方,也是数据流的来源和去向之一。
对数据存储的描述
 数据存储描述={数据存储名,说明,编号,输的数据流 ,
输出的数据流, 组成:{数据结构},数据量, 存取频度,存取方式}
存取频度:每小时、每天或每周存取次数,每次存取的数据量等信息
存取方法:批处理 / 联机处理;检索 / 更新;顺序检索 / 随机检索
输入的数据流:数据来源
输出的数据流:数据去向

处理过程

处理过程的具体处理逻辑一般用判定表或判定树来描述。数据字典中只需要描述处理过程的说明性信息
处理过程说明性信息的描述
处理过程描述={处理过程名,说明,输入:{数据流},
输出:{数据流},处理:{简要说明}}
简要说明:说明该处理过程的功能及处理要求
功能:该处理过程用来做什么
处理要求:处理频度要求,如单位时间里处理多少事务,多少数据量、响应时间要求等
处理要求是后面物理设计的输入及性能评价的标准

小结

把需求收集和分析作为数据库设计的第一阶段是十分重要的。
第一阶段收集的基础数据(用数据字典来表达)是下一步进行概念设计的基础。
强调两点:

  • 设计人员应充分考虑到可能的扩充和改变,使设计易于更改,系统易于扩充
  • 必须强调用户的参与

概念模型和ER模型

将需求分析得到的用户需求抽象为信息结构(即概念模型)的过程就是概念结构设计
概念模型的特点
能真实、充分地反映现实世界,是现实世界的一个真实模型。
易于理解,从而可以用它和不熟悉计算机的用户交换意见。
易于更改,当应用环境和应用要求改变时,容易对概念模型修改和扩充。
易于向关系、网状、层次等各种数据模型转换
描述概念模型的工具
E-R模型

E-R图先前也学习过了,而且当时我就说这是一个重点

两实体型之间的联系:1-1,1-n,m-n
同一个实体集内的各实体之间也可以存在一对一、一对多、多对多的联系。

E-R模型

联系的度:参与联系的实体型的数目

  • 2个实体型之间的联系度为2,也称为二元联系;
  • 3个实体型之间的联系度为3,称为三元联系;
  • N个实体型之间的联系度为N,也称为N元联系

E-R图提供了表示实体型、属性和联系的方法:

  • 实体型:用矩形表示,矩形框内写明实体名。
  • 属性:用椭圆形表示,并用无向边将其与相应的实体型连接起来。
  • 联系:用菱形表示,菱形框内写明联系名,并用无向边分别与有关实体型连接起来,同时在无向边旁标上联系的类型(1∶1,1∶n或m∶n)。
  • 联系可以具有属性,打我脸了,我之前说联系应该没有属性的

画E-R图步骤:

  • 画实体及其属性图
  • 画实体及其联系图
  • 将两者拼接起来,画完整的实体-联系图

ISA联系

有的实体型是某个实体型的子类型,这种父类-子类联系称为ISA联系,表示“is a”语义。用△表示。
ISA联系的性质: 子类继承了父类的所有属性,子类也可以有自己的属性。
分类属性是父实体型的一个属性
分类属性的值把父实体型中的实体分派到子实体型中
不相交约束:描述父类中的一个实体不能同时属于多个子类中的实体集。即-一个父类中的实体最多属于一个子类实体集。
用ISA联系符号三角形的-一个叉号“X"来表示。
可重叠约束:父类中的一个实体能同时属于多个子类中的实体集。子类符号中没有叉号表示是可重叠的。

完备性约束

描述父类中的一个实体是否必须是某一个子类中的实体。

  • 如果是,则叫做完全特化( total specialization )
  • 否则叫做部分特化( partial specialization)

完全特化用父类到子类的双线连接来表示
部分特化用父类到子类的单线连接来表示

基数约束

说明实体型中的任何一个实体可以在联系中出现的最少次数和最多次数,在联系的基础上加上量
对实体之间一对一、 一对多、多对多联系的细化。
约束用一个数对 min…max表示,0 ≤ min ≤ max。例如,0…1, 1…3, 1…,, 其中代表无穷大。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFzcJ0Mk-1687783555709)(null)]

其实怎么讲,联系应该广义上来说是种双向关系吧,之所以把图拿上来是它标数字的位置需要注意一下,是与它相对的横线处标

min=1的约束叫做强制参与约束,即被施加基数约束的实体型中的每个实体都要参与联系;
min=0的约束叫做非强制参与约束,被施加基数约束的实体型中的实体可以出现在联系中,也可以不出现在联系

Part-of联系

描述某个实体型是另外一个实体型的一部分。
Part-of 联系可以分为两种情况:
非独占的Part-of联系,简称非独占联系
整体实体如果被破坏,另一部分实体仍然可以独立存在
独占的Part-of联系, 简称独占联系
整体实体如果被破坏,部分实体不能存在

Part-of联系如何表示?
用非强制参与联系表示非独占的Part-of联系
用弱实体类型和识别联系来表示独占联系编号

如果一个实体型的存在依赖于其它实体型的存在,则这个实体型叫做弱实体型,否则叫做强实体型。
用弱实体类型和识别联系来表示独占联系双矩形表示弱实体型,用双菱型表示识别联系。

概念结构设计

概念结构设计的方法:
自顶向下——首先定义全局概念结构的框架,然后逐步细化
自底向上——首先定义各局部应用的概念结构,然后将它们集成起来,得到全局概念结构
逐步扩张——首先定义最重要的核心概念结构,然后向外扩充,以滚雪球的方式逐步生成其他概念结构,直至总体概念结构
混合策略——将自顶向下和自底向上相结合,用自顶向下策略设计一个全局概念结构的框架,以它为骨架集成由自底向上策略中设计的各局部概念结构。
与这些天学习的软件过程模型十分贴合

常用策略:

  • 自顶向下地进行需求分析
  • 自底向上地设计概念结构

概念结构设计的步骤:
自底向上设计概念结构的步骤

  • 第1步:抽象数据并设计局部视图
  • 第2步:集成局部视图,得到全局概念结构

实体与属性的划分

实体与属性的划分原则
为了简化E-R图的处置,现实世界的事物能作为属性对待的,尽量作为属性对待。
两条准则:

  • 作为属性,不能再具有需要描述的性质。属性必须是不可分的数据项,不能包含其他属性。
  • 属性不能与其他实体具有联系,即E-R图中所表示的联系是实体之间的联系。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMgyTRcR-1687783559630)(null)]

实体属性的划分确实可以帮助我们更好地只做E-R图,后面的章节也是在讲如何设计E-R图,使其变得更好。

E-R图的集成

E-R图的集成一般需要分两步

  • 合并。解决各分E-R图之间的冲突,将分E-R图合并起来生成初步E-R图。
  • 修改和重构。消除不必要的冗余,生成基本E-R图

子系统E-R图之间的冲突主要有三类:

  • 属性冲突
  • 命名冲突
  • 结构冲突
属性冲突

属性域冲突,即属性值的类型、取值范围或取值集合不同。
例如零件号,有的部门把它定义为整数,有的部门把它定义为字符型。
年龄,某些部门以出生日期形式表示职工的年龄,而另一些部门用整数表示职工的年龄。
属性取值单位冲突。
例如,零件的重量有的以公斤为单位,有的以斤为单位,有的以克为单位

命名冲突

同名异义,即不同意义的对象在不同的局部应用中具有相同的名字。
异名同义(一义多名),即同一意义的对象在不同的局部应用中具有不同的名字。
如对科研项目,财务科称为项目,科研处称为课题,生产管理处称为工程。
命名冲突
可能发生在实体、联系一级上
也可能发生在属性一级上
通过讨论、协商等行政手段加以解决

结构冲突

同一对象在不同应用中具有不同的抽象。
例如,职工在某一局部应用中被当作实体,而在另一局部应用中则被当作属性。
解决方法:把属性变换为实体或把实体变换为属性,使同一对象具有相同的抽象。但仍然要遵循实体与属性的划分原则。
同一实体在不同子系统的E-R图中所包含的属性个数和属性排列次序不完全相同。
解决方法:使该实体的属性取各子系统的E-R图中属性的并集,再适当调整属性的次序。
实体间的联系在不同的E-R图中为不同的类型。
实体E1与E2在一个E-R图中是多对多联系,在另一个E-R图中是一对多联系
解决方法:根据应用的语义对实体联系的类型进行综合或调整。

E-R图修改重构

消除不必要的冗余,设计基本E-R图
所谓冗余的数据是指可由基本数据导出的数据,冗余的联系是指可由其他联系导出的联系。
消除冗余主要采用分析方法,即以数据字典和数据流图为依据,根据数据字典中关于数据项之间逻辑关系的说明来消除冗余。

用规范化理论来消除冗余

由于规范化理论受到泛关系假设的限制,应注意下面两个问题:
冗余的联系一定在D中,而D中的联系不一定是冗余的;
当实体之间存在多种联系时,要将实体之间的联系在形式上加以区分。
如部门和职工之间另一个一对一的联系可以表示为:负责人.职工号→部门号,部门号→部门号负责人.职工号

逻辑结构设计

把概念结构设计阶段设计好的基本E-R图转换为与选用数据库管理系统产品所支持的数据模型相符合的逻辑结构
转换内容
E-R图由实体型、实体的属性和实体型之间的联系三个要素组成
关系模型的逻辑结构是一组关系模式的集合
将E-R图转换为关系模型:将实体型、实体的属性和实体型之间的联系转化为关系模式

转换原则

实体型

一个实体型转换为一个关系模式。

  • 关系的属性:实体的属性
  • 关系的码:实体的码
1:1联系

一个1:1联系可以转换为一个独立的关系模式,也可以与任意一端对应的关系模式合并。

转换为一个独立的关系模式
关系的属性:与该联系相连的各实体的码以及联系本身的属性
关系的候选码:每个实体的码均是该关系的候选码

与某一端实体对应的关系模式合并
合并后关系的属性:加入对应关系的码和联系本身的属性
合并后关系的码:不变

1:n联系

一个1:n联系可以转换为一个独立的关系模式,也可以与n端对应的关系模式合并。

转换为一个独立的关系模式
关系的属性:与该联系相连的各实体的码以及联系本身的属性
关系的码:n端实体的码

与n端对应的关系模式合并
合并后关系的属性:在n端关系中加入1端关系的码和联系本身的属性
合并后关系的码:不变
可以减少系统中的关系个数

m:n联系

一个m:n联系转换为一个关系模式
关系的属性:与该联系相连的各实体的码以及联系本身的属性
关系的码:各实体码的组合
[例]“选修”联系是一个m:n联系,可以将它转换为如下关系模式,其中学号与课程号为关系的组合码:
选修(学号,课程号,成绩)

多元联系

三个或三个以上实体间的一个多元联系转换为一个关系模式。
关系的属性:与该多元联系相连的各实体的码以及联系本身的属性
关系的码:各实体码的组合

关系模式合并

具有相同码的关系模式可合并
目的:减少系统中的关系个数
合并方法:
将其中一个关系模式的全部属性加入到另一个关系模式中
然后去掉其中的同义属性(可能同名也可能不同名)
适当调整属性的次序

E-R图向关系模型转化

数据模型优化

数据库逻辑设计的结果不是唯一的。
得到初步数据模型后,还应该适当地修改、调整数据模型的结构,以进一步提高数据库应用系统的性能,这就是数据模型的优化。
关系数据模型的优化通常以规范化理论为指导。

语义决定是哪一种范式
确定数据依赖
按需求分析阶段所得到的语义,分别写出每个关系模式内部各属性之间的数据依赖以及不同关系模式属性之间数据依赖。
对于各个关系模式之间的数据依赖进行极小化处理,消除冗余的联系。
按照数据依赖的理论对关系模式进行分析,考察是否存在部分函数依赖、传递函数依赖、多值依赖等,确定各关系模式分别属于第几范式。
按照需求分析阶段得到的各种应用对数据处理的要求,分析对于这样的应用环境这些模式是否合适,确定是否要对它们进行合并或分解。

并不是规范化程度越高的关系就越优
当查询经常涉及两个或多个关系模式的属性时,系统必须经常地进行连接运算
连接运算的代价是相当高的
因此在这种情况下,第二范式甚至第一范式也许是适合的。
非BCNF的关系模式虽然会存在不同程度的更新异常,但如果在实际应用中对此关系模式只是查询,并不执行更新操作,就不会产生实际影响。
对于一个具体应用来说,到底规范化进行到什么程度,需要权衡响应时间和潜在问题两者的利弊才能决定

关系模式分解

对关系模式进行必要分解,提高数据操作效率和存储空间的利用率。
常用分解方法

  • 水平分解
  • 垂直分解
水平分解

把(基本)关系的元组分为若干子集合,定义每个子集合为一个子关系,以提高系统的效率。
如何分解

  • 对符合“80/20原则”的,把经常被使用的数据(约20%)水平分解出来,形成一个子关系。
  • 水平分解为若干子关系,使每个事务存取的数据对应一个子关系。
垂直分解

把关系模式R的属性分解为若干子集合,形成若干子关系模式。
垂直分解的原则:经常在一起使用的属性从R中分解出来形成一个子关系模式
垂直分解的优点

  • 可以提高某些事务的效率

垂直分解的缺点

  • 可能使另一些事务不得不执行连接操作,降低了效率

垂直分解的适用范围

  • 取决于分解后R上的所有事务的总效率是否得到了提高

进行垂直分解的方法

  • 简单情况:直观分解
  • 复杂情况:用模式分解算法
    • 垂直分解必须不损失关系模式的语义(保持无损连接性和保持函数依赖)

设计用户子模式

定义数据库模式主要是从系统的时间效率、空间效率、易维护等角度出发。
定义用户外模式时应该更注重考虑用户的习惯与方便。包括三个方面:

  • 使用更符合用户习惯的别名
  • 针对不同级别的用户定义不同的视图,以保证系统的安全性
  • 简化用户对系统的使用

数据库管理系统适应性转换

一般的数据模型还需要向特定数据库管理系统规定的模型进行转换。
转换的主要依据是所选用的数据库管理系统的功能及限制。没有通用规则。
对于关系模型来说,这种转换通常都比较简单。

物理结构设计

数据库物理设计

数据库在物理设备上的存储结构与存取方法称为数据库的物理结构,它依赖于选定的数据库管理系统。
为一个给定的逻辑数据模型选取一个最适合应用要求的物理结构的过程,就是数据库的物理设计。
数据库物理设计的步骤

  • 确定数据库的物理结构,在关系数据库中主要指存取方法和存储结构;
  • 对物理结构进行评价,评价的重点是时间和空间效率

若评价结果满足原设计要求,则可进入到物理实施阶段。否则,就需要重新设计或修改物理结构,有时甚至要返回逻辑设计阶段修改数据模型。

数据库物理设计的内容和方法

  • 设计物理数据库结构的准备工作:
    • 充分了解应用环境,详细分析要运行的事务,以获得选择物理数据库设计所需参数。
    • 充分了解所用关系型数据库管理系统的内部特征,特别是系统提供的存取方法和存储结构。
  • 关系数据库物理设计的内容:
    • 为关系模式选择存取方法
    • 设计关系、索引等数据库文件的物理存储结构
  • 物理数据库设计所需参数
    • 数据库查询事务
      • 查询的关系
      • 查询条件所涉及的属性
      • 连接条件所涉及的属性
      • 查询的投影属性
    • 数据更新事务
      • 被更新的关系
      • 每个关系上的更新操作条件所涉及的属性
      • 修改操作要改变的属性值
    • 每个事务在各关系上运行的频率和性能要求

关系模式存取方法选择

数据库系统是多用户共享的系统,对同一个关系要建立多条存取路径才能满足多用户的多种应用要求。
物理结构设计的任务之一是根据关系数据库管理系统支持的存取方法确定选择哪些存取方法。

数据库管理系统常用存取方法:
B+树索引存取方法
Hash索引存取方法
聚簇存取方法

B+树索引存取方法的选择

选择索引存取方法的主要内容:根据应用要求确定
对哪些属性列建立索引
对哪些属性列建立组合索引
对哪些索引要设计为唯一索引
选择索引存取方法的一般规则
如果一个(或一组)属性经常在查询条件中出现,则考虑在这个(或这组)属性上建立索引(或组合索引)
如果一个属性经常作为最大值和最小值等聚集函数的参数,则考虑在这个属性上建立索引
如果一个(或一组)属性经常在连接操作的连接条件中出现,则考虑在这个(或这组)属性上建立索引
关系上定义的索引数过多会带来较多的额外开销(维护、查找索引的开销)

HASH存取方法的选择

选择Hash存取方法的规则
如果一个关系的属性主要出现在等值连接条件中或主要出现在等值比较选择条件中,而且满足下列两个条件之一
该关系的大小可预知,而且不变;
该关系的大小动态改变,但所选用的数据库管理系统提供了动态Hash存取方法。

聚簇

为了提高某个属性(或属性组)的查询速度,把这个或这些属性(称为聚簇码)上具有相同值的元组集中存放在连续的物理块中称为聚簇。
该属性(或属性组)称为聚簇码(cluster key)
聚簇的用途:大大提高按聚簇属性进行查询的效率
[例] 假设学生关系按所在系建有索引,现在要查询信息系的所有学生名单。
计算机系的500名学生分布在500个不同的物理块上时,至少要执行500次I/O操作。
如果将同一系的学生元组集中存放,则每读一个物理块可得到多个满足查询条件的元组,从而显著地减少了访问磁盘的次数。

聚簇
既适用于单个关系独立聚簇
也适用于经常进行连接操作的多个关系
把多个连接的元组按连接属性值聚集存放
从而实现多个关系的“预连接”,提高连接操作的效率。
选择聚簇存储方法,即确定需要建立多少个聚簇,每个聚簇中包含哪些关系
一个数据库可以建立多个聚簇,一个关系只能加入一个聚簇。

设计候选聚簇
常在一起进行连接操作的关系可以建立组合聚簇
如果一个关系的一组属性经常出现在相等比较条件中,则该单个关系可建立聚簇;
如果一个关系的一个(或一组)属性上的值重复率很高,则此单个关系可建立聚簇。
检查候选聚簇中的关系,取消其中不必要的关系
从聚簇中删除经常进行全表扫描的关系
从聚簇中删除更新操作远多于连接操作的关系
从聚簇中删除重复出现的关系
当一个关系同时加入多个聚簇时,必须从这多个聚簇方案(包括不建立聚簇)中选择一个较优的,即在这个聚簇上运行各种事务的总代价最小。

聚簇的局限性
聚簇只能提高某些特定应用的性能
建立与维护聚簇的开销相当大
对已有关系建立聚簇,将导致关系中元组的物理存储位置移动,并使此关系上原有的所有索引无效,必须重建。
当一个元组的聚簇码改变时,该元组的存储位置也要做相应改变。

当通过聚簇码进行访问或连接是该关系的主要应用,与聚簇码无关的其他访问很少或者是次要的时,可以使用聚簇
尤其当SQL语句中包含有与聚簇码有关的ORDER BY, GROUP BY, UNION, DISTINCT等子句或短语时,使用聚簇特别有利,可以省去或减化对结果集的排序操作

确定数据库的存储结构

确定数据库物理结构主要指确定数据的存放位置和存储结构,包括:确定关系、索引、聚簇、日志、备份等的存储安排和存储结构,确定系统配置等。
影响数据存放位置和存储结构的因素:

  • 硬件环境
  • 应用需求:
    • 存取时间
    • 存储空间利用率
    • 维护代价
    • 这三个方面常常是相互矛盾的,必须进行权衡,选择一个折中方案

确定数据的存放位置:
基本原则
根据应用情况将
易变部分与稳定部分分开存放
经常存取部分与存取频率较低部分分开存放
[例]
可以将比较大的表分别放在两个磁盘上,以加快存取速度,这在多用户环境下特别有效。
可以将日志文件与数据库对象(表、索引等)放在不同的磁盘以改进系统的性能。

确定系统配置:
数据库管理系统一般都提供了一些存储分配参数
同时使用数据库的用户数
同时打开的数据库对象数
内存分配参数
缓冲区分配参数(使用的缓冲区长度、个数)
存储分配参数
物理块的大小
物理块装填因子
时间片大小
数据库的大小
锁的数目

系统都为这些变量赋予了合理的缺省值。
在进行物理设计时需要根据应用环境确定这些参数值,以使系统性能最优。
在物理设计时对系统配置变量的调整只是初步的,要根据系统实际运行情况做进一步的调整,以切实改进系统性能。

评价物理结构

对数据库物理设计过程中产生的多种方案进行评价,从中选择一个较优的方案作为数据库的物理结构。
评价方法
定量估算各种方案

  • 存储空间
  • 存取时间
  • 维护代价

对估算结果进行权衡、比较,选择出一个较优的合理的物理结构。

数据库实施与维护

数据的载入

数据库结构建立好后,就可以向数据库中装载数据了。组织数据入库是数据库实施阶段最主要的工作。
数据装载方法

  • 人工方法
  • 计算机辅助数据入库

应用程序的调试

数据库应用程序的设计应该与数据设计并行进行
在组织数据入库的同时还要调试应用程序
应用程序的设计、编码和调试的方法、步骤在软件工程等课程中有详细讲解,这里就不赘述了

数据库试运行

应用程序调试完成,并且已有一小部分数据入库后,就可以开始对数据库系统进行联合调试,也称数据库的试运行。
主要工作包括:
功能测试:实际运行应用程序,执行对数据库的各种操作,测试应用程序的各种功能。
性能测试:测量系统的性能指标,分析是否符合设计目标

数据库性能指标的测量
数据库物理设计阶段在评价数据库结构估算时间、空间指标时,作了许多简化和假设,忽略了许多次要因素,因此结果必然很粗糙。
数据库试运行则是要实际测量系统的各种性能指标(不仅是时间、空间指标),如果结果不符合设计目标,则需要返回物理设计阶段,调整物理结构,修改参数;有时甚至需要返回逻辑设计阶段,调整逻辑结构

数据库的试运行的注意事项:
1.数据的分期入库:
重新设计物理结构甚至逻辑结构,会导致数据重新入库
由于数据入库工作量实在太大,所以可以采用分期输入数据的方法
先输入小批量数据供先期联合调试使用
待试运行基本合格后再输入大批量数据
逐步增加数据量,逐步完成运行评价
2.数据库的转储和恢复:
在数据库试运行阶段,系统还不稳定,硬、软件故障随时都可能发生
系统的操作人员对新系统还不熟悉,误操作也不可避免
因此必须做好数据库的转储和恢复工作,尽量减少对数据库的破坏

数据库运行和维护

在数据库运行阶段,对数据库经常性的维护工作主要是由数据库管理员完成的,包括:
数据库的转储和恢复
数据库的安全性、完整性控制
数据库性能的监督、分析和改进
数据库的重组织与重构造

数据库的转储和恢复

数据库管理员要针对不同的应用要求制定不同的转储计划,定期对数据库和日志文件进行备份。
一旦发生介质故障,即利用数据库备份及日志文件备份,尽快将数据库恢复到某种一致性状态。

数据库的安全性、完整性控制

初始定义
数据库管理员根据用户的实际需要授予不同的操作权限
根据应用环境定义不同的完整性约束条件
修改定义
当应用环境发生变化,对安全性的要求也会发生变化,数据库管理员需要根据实际情况修改原有的安全性控制
由于应用环境发生变化,数据库的完整性约束条件也会变化,也需要数据库管理员不断修正,以满足用户要求

数据库性能的监督、分析和改进

在数据库运行过程中,数据库管理员必须监督系统运行,对监测数据进行分析,找出改进系统性能的方法。
利用监测工具获取系统运行过程中一系列性能参数的值
通过仔细分析这些数据,判断当前系统是否处于最佳运行状态
如果不是,则需要通过调整某些参数来进一步改进数据库性能

数据库的重组织

1.为什么要重组织数据库
数据库运行一段时间后,由于记录的不断增、删、改,会使数据库的物理存储变坏,从而降低数据库存储空间的利用率和数据的存取效率,使数据库的性能下降。
2.重组织的形式
全部重组织
部分重组织
只对频繁增、删的表进行重组织
3.重组织的目标
提高系统性能
4.重组织的工作
按原设计要求
重新安排存储位置
回收垃圾
减少指针链
数据库的重组织不会改变原设计的数据逻辑结构和物理结构
5.数据库管理系统一般都提供了供重组织数据库使用的实用程序,帮助数据库管理员重新组织数据库。

数据库的重构造
  • 为什么要进行数据库的重构造
    • 数据库应用环境发生变化,会导致实体及实体间的联系也发生相应的变化,使原有的数据库设计不能很好地满足新的需求
      • 增加新的应用或新的实体
      • 取消某些已有应用
      • 改变某些已有应用
  • 重构造的主要工作
    • 根据新环境调整数据库的模式和内模式
      • 增加或删除某些数据项
      • 改变数据项的类型
      • 增加或删除某个表
      • 改变数据库的容量
      • 增加或删除某些索引

重构造数据库的程度是有限的:
若应用变化太大,已无法通过重构数据库来满足新的需求,或重构数据库的代价太大,则表明现有数据库应用系统的生命周期已经结束,应该重新设计新的数据库应用系统了,真是沉痛的消息。

Part8——其他SQL

仅作了解,确实出不了考点

面向驱动的数据库编程

JAVA世界:JDBC
微软世界:ODBC,OLE DB等
Python世界:Python DB-API + 各数据库模块(如PyMySQL,SQLite3等)

过程化SQL

过程化SQL

  • SQL的扩展
  • 增加了过程化语句功能
  • 基本结构是块
    • 块之间可以互相嵌套
    • 每个块完成一个逻辑操作

块结构

定义部分
DECLARE 变量、常量、游标、异常等
定义的变量、常量等只能在该基本块中使用
当基本块执行结束时,定义就不再存在
执行部分
BEGIN
SQL语句、过程化SQL的流程控制语句
EXCEPTION
异常处理部分
END;

变量和常量定义

变量定义
变量名 数据类型 [[NOT NULL]:=初值表达式]或
变量名 数据类型 [[NOT NULL] 初值表达式]
常量定义
常量名 数据类型 CONSTANT :=常量表达式
常量必须要给一个值,并且该值在存在期间或常量的作用域内不能改变。如果试图修改它,过程化SQL将返回一个异常
赋值语句
变量名称 :=表达式

流程控制——条件控制语句

IF condition THEN
Sequence_of_statements;
END IF;
IF condition THEN
Sequence_of_statements1;
ELSE
Sequence_of_statements2;
END IF;
在THEN和ELSE子句中还可以再包含IF语句,即IF语句可以嵌套

流程控制——循环控制语句

简单的循环语句LOOP
LOOP
Sequence_of_statements; //提供EXIT、BREAK或LEAVE等循环结束语句
END LOOP;
WHILE-LOOP
WHILE condition LOOP
Sequence_of_statements;
END LOOP;
FOR-LOOP
FOR count IN [REVERSE] bound1 … bound2 LOOP
Sequence_of_statements;
END LOOP;

流程控制——错误处理

如果过程化SQL在执行时出现异常,则应该让程序在产生异常的语句处停下来,根据异常的类型去执行异常处理语句
SQL标准对数据库服务器提供什么样的异常处理做出了建议,要求过程化SQL管理器提供完善的异常处理机制

存储过程和函数

存储过程

过程化SQL块类型
命名块
编译后保存在数据库中,可以被反复调用,运行速度较快,过程和函数是命名块
匿名块
每次执行时都要进行编译,它不能被存储到数据库中,也不能在其他过程化SQL块中调用

存储过程:由过程化SQL语句书写的过程,经编译和优化后存储在数据库服务器中,使用时只要调用即可。
存储过程的优点
运行效率高
降低了客户机和服务器之间的通信量
方便实施企业规则

存储过程的用户接口

创建存储过程
CREATE OR REPLACE PROCEDURE 过程名([参数1,参数2,…]) AS <过程化SQL块>;
过程名:数据库服务器合法的对象标识
参数列表:用名字来标识调用时给出的参数值,必须指定值的数据类型。参数也可以定义输入参数、输出参数或输入/输出参数,默认为输入参数
过程体:是一个<过程化SQL块>,包括声明部分和可执行语句部分

[8.8] 利用存储过程来实现下面的应用:从账户1转指定数额的款项到账户2中。

	CREATE OR REPLACE PROCEDURE TRANSFER(inAccount INT,outAccount  INT,amount FLOAT) 
       /*定义存储过程TRANSFER,其参数为转入账户、转出账户、转账额度*/
	AS DECLARE		/*定义变量*/
		totalDepositOut Float;
     	totalDepositIn Float;
		inAccountnum INT;
    BEGIN                         	    /*检查转出账户的余额 */	                       
	       SELECT Total INTO totalDepositOut FROM Accout 
	       WHERE accountnum=outAccount; 
	       IF totalDepositOut IS NULL THEN   
                                   		 /*如果转出账户不存在或账户中没有存款*/
	                ROLLBACK; 	   /*回滚事务*/
	                RETURN;
	       END IF; 
 	IF totalDeposit Out< amount THEN    	/*如果账户存款不足*/
		ROLLBACK; 				/*回滚事务*/
		RETURN;
	END IF;
	SELECT Accountnum INTO inAccountnum FROM Account
	WHERE accountnum=inAccount;
	IF inAccount IS NULL THEN  		/*如果转入账户不存在*/                        
		ROLLBACK; 	         	 		/*回滚事务*/
		RETURN;
	ENDIF;
	UPDATE Account SET total=total-amount 	WHERE accountnum=outAccount;
 				/* 修改转出账户余额,减去转出额 */
	UPDATE Account SET total=total + amount 
	WHERE   accountnum=inAccount; 
				/* 修改转入账户余额,增加转入额 */
	COMMIT;                       	/* 提交转账事务 */
END;

执行存储过程
CALL/PERFORM PROCEDURE 过程名([参数1,参数2,…]);
使用CALL或者PERFORM等方式激活存储过程的执行
在过程化SQL中,数据库服务器支持在过程体中调用其他存储过程

[8.9] 从账户0100381586810000元到01003813828账户中。
    CALL PROCEDURE TRANSFER(01003813828,01003815868,10000);   

修改存储过程
ALTER PROCEDURE 过程名1 RENAME TO 过程名2;
删除存储过程
DROP PROCEDURE 过程名();

函数

函数的定义语句格式
CREATE OR REPLACE FUNCTION 函数名 ([参数1,参数2,…]) RETURNS <类型> AS <过程化SQL块>;
函数的执行语句格式
CALL/SELECT 函数名 ([参数1,参数2,…]);
修改函数
重命名
ALTER FUNCTION 过程名1 RENAME TO 过程名2;
重新编译
ALTER FUNCTION 过程名 COMPILE;

Part9——数据库维护

看看概念,图个乐

事务

事务(Transaction)是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
事务和程序是两个概念

  • 在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序
  • 一个程序通常包含多个事务

事务是恢复和并发控制的基本单位

定义事务

显示定义方法:
BEGIN TRANSACTION
SQL 语句1
SQL 语句2

COMMIT/ROLLBACK

COMMIT:
事务正常结束
提交事务的所有操作(读+更新)
事务中所有对数据库的更新写回到磁盘上的物理数据库中

ROLLBACK:
事务异常终止
事务运行的过程中发生了故障,不能继续执行
系统将事务中对数据库的所有已完成的操作全部撤销
事务滚回到开始时的状态

隐式方式
当用户没有显式地定义事务时,
数据库管理系统按缺省规定自动划分事

事务的特性

事务的ACID特性:

  • 原子性(Atomicity)
    • 事务是数据库的逻辑工作单位
      • 事务中包括的诸操作要么都做,要么都不做
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持续性(Durability)
原子性&&一致性

事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态
一致性状态
数据库中只包含成功事务提交的结果
不一致状态
数据库系统运行中发生故障,有些事务尚未完成就被迫中断;
这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态

银行转帐:从帐号A中取出一万元,存入帐号B。
定义一个事务,该事务包括两个操作
这两个操作要么全做,要么全不做//原子性
全做或者全不做,数据库都处于一致性状态。//一致性
如果只做一个操作,用户逻辑上就会发生错误,少了一万元,数据库就处于不一致性状态。

隔离性&&持续性

隔离性:

  • 一个事务的执行不能被其他事务干扰
  • 一个事务内部的操作及使用的数据对其他并发事务是隔离的
  • 并发执行的各个事务之间不能互相干扰

持续性也称永久性(Permanence)

  • 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
  • 接下来的其他操作或故障不应该对其执行结果有任何影响
其他

保证事务ACID特性是事务处理的任务

破坏事务ACID特性的因素

  • 多个事务并行运行时,不同事务的操作交叉执行
    • 数据库管理系统必须保证多个事务的交叉运行不影响这些事务的隔离性
  • 事务在运行过程中被强行停止
    • 数据库管理系统必须保证被强行终止的事务对数据库和其他事务没有任何影响

故障和数据库恢复

故障是不可避免的!!!牢记这句真理名言。

  • 计算机硬件故障
  • 软件的错误
  • 操作员的失误
  • 恶意的破坏

故障的影响:

  • 运行事务非正常中断,影响数据库中数据的正确性
  • 破坏数据库,全部或部分丢失数据

数据库的恢复

  • 数据库管理系统必须具有把数据库从错误状态恢复到某一已知的正确状态(亦称为一致状态或完整状态)的功能,这就是数据库的恢复管理系统对故障的对策

恢复子系统是数据库管理系统的一个重要组成部分
恢复技术是衡量系统优劣的重要指标

故障

故障的种类

  • 事务内部的故障
  • 系统故障
  • 介质故障
  • 计算机病毒

各类故障,对数据库的影响有两种可能性
一是数据库本身被破坏
二是数据库没有被破坏,但数据可能不正确,这是由于事务的运行被非正常终止造成的。

事务内部故障

事务内部更多的故障是非预期的,是不能由应用程序处理的。
运算溢出
并发事务发生死锁而被选中撤销该事务
违反了某些完整性限制而被终止等
事务故障仅指这类非预期的故障

事务故障的恢复

事务故障意味着

  • 事务没有达到预期的终点(COMMIT或者显式的ROLLBACK)
  • 数据库可能处于不正确状态。

事务故障的恢复:事务撤消(UNDO)

  • 强行回滚(ROLLBACK)该事务
  • 撤销该事务已经作出的任何对数据库的修改,使得该事务象根本没有启动一样
系统故障

系统故障,称为软故障,是指造成系统停止运转的任何事件(特定类型的硬件错误(如CPU故障)、操作系统故障、数据库管理系统代码错误、系统断电),使得系统要重新启动。
整个系统的正常运行突然被破坏
所有正在运行的事务都非正常终止
不破坏数据库
内存中数据库缓冲区的信息全部丢失

系统故障恢复

发生系统故障时,一些尚未完成的事务的结果可能已送入物理数据库,造成数据库可能处于不正确状态。
恢复策略:系统重新启动时,恢复程序让所有非正常终止的事务回滚,强行撤消(UNDO)所有未完成事务
发生系统故障时,有些已完成的事务可能有一部分甚至全部留在缓冲区,尚未写回到磁盘上的物理数据库中,系统故障使得这些事务对数据库的修改部分或全部丢失
恢复策略:系统重新启动时,恢复程序需要重做(REDO)所有已提交的事务

介质故障

介质故障,称为硬故障,指外存故障

  • 磁盘损坏
  • 磁头碰撞
  • 瞬时强磁场干扰

介质故障破坏数据库或部分数据库,并影响正在存取这部分数据的所有事务
介质故障比前两类故障的可能性小得多,但破坏性大得多

计算机病毒

计算机病毒

  • 一种人为的故障或破坏,是一些恶作剧者研制的一种计算机程序
  • 可以繁殖和传播,造成对计算机系统包括数据库的危害

计算机病毒已成为计算机系统的主要威胁,自然也是数据库系统的主要威胁
数据库一旦被破坏仍要用恢复技术把数据库加以恢复

恢复

恢复操作的基本原理:冗余
利用存储在系统别处的冗余数据来重建数据库中已被破坏或不正确的那部分数据
恢复的实现技术:复杂
一个大型数据库产品,恢复子系统的代码要占全部代码的10%以上
恢复机制涉及的关键问题
如何建立冗余数据(数据转储,登记日志文件)
如何利用这些冗余数据实施数据库恢复

数据转储和日志文件

数据转储

也就是备份吧

转储是指数据库管理员定期地将整个数据库复制到磁带、磁盘或其他存储介质上保存起来的过程
备用的数据文本称为后备副本(backup)或后援副本
数据库遭到破坏后可以将后备副本重新装入
重装后备副本只能将数据库恢复到转储时的状态
要想恢复到故障发生时的状态,必须重新运行自转储以后的所有更新事务

系统在Ta时刻停止运行事务,进行数据库转储
在Tb时刻转储完毕,得到Tb时刻的数据库一致性副本
系统运行到Tf时刻发生故障
为恢复数据库,首先由数据库管理员重装数据库后备副本,将数据库恢复至Tb时刻的状态
重新运行自Tb ~ Tf时刻的所有更新事务,把数据库恢复到故障发生前的一致状态

转储方法
静态转储

在系统中无运行事务时进行的转储操作
转储开始时数据库处于一致性状态
转储期间不允许对数据库的任何存取、修改活动
得到的一定是一个数据一致性的副本

优点:实现简单
缺点:降低了数据库的可用性

  • 转储必须等待正运行的用户事务结束
  • 新的事务必须等转储结束
动态存储

转储操作与用户事务并发进行
转储期间允许对数据库进行存取或修改

优点
不用等待正在运行的用户事务结束
不会影响新事务的运行

动态转储的缺点
不能保证副本中的数据正确有效
例在转储期间的某时刻Tc ,系统把数据A=100转储到磁带上,而在下一时刻Td ,某一事务将A改为200,后备副本上的A过时了

利用动态转储得到的副本进行故障恢复
需要把动态转储期间各事务对数据库的修改活动登记下来,建立日志文件
后备副本加上日志文件就能把数据库恢复到某一时刻的正确状态

海量转储与增量转储

海量转储: 每次转储全部数据库
增量转储: 只转储上次转储后更新过的数据,git?
海量转储与增量转储比较:

  • 从恢复角度看,使用海量转储得到的后备副本进行恢复往往更方便
  • 如果数据库很大,事务处理又十分频繁,则增量转储方式更实用更有效

日志文件

日志文件(log file)是用来记录事务对数据库的更新操作的文件
日志文件的格式
以记录为单位的日志文件
以数据块为单位的日志文件
用途

  • 进行事务故障恢复
  • 进行系统故障恢复
  • 协助后备副本进行介质故障恢复
以记录为单位日志文件

日志文件中的一个日志记录 (log record)包含

  • 各个事务的开始标记(BEGIN TRANSACTION)
  • 各个事务的结束标记(COMMIT或ROLLBACK)
  • 各个事务的所有更新操作

以记录为单位的日志文件,每条日志记录的内容

  • 事务标识(标明是哪个事务)
  • 操作类型(插入、删除或修改)
  • 操作对象(记录内部标识)
  • 更新前数据的旧值(对插入操作而言,此项为空值)
  • 更新后数据的新值(对删除操作而言, 此项为空值)
以数据块为单位的日志文件

以数据块为单位的日志文件,每条日志记录的内容

  • 事务标识
  • 被更新的数据块
日志文件作用

具体作用

  • 事务故障恢复和系统故障恢复必须用日志文件。
  • 在动态转储方式中必须建立日志文件,后备副本和日志文件结合起来才能有效地恢复数据库。
  • 在静态转储方式中,也可以建立日志文件。
    • 当数据库毁坏后可重新装入后援副本把数据库恢复到转储结束时刻的正确状态
    • 利用日志文件,把已完成的事务进行重做处理
    • 对故障发生时尚未完成的事务进行撤销处理
    • 不必重新运行那些已完成的事务程序就可把数据库恢复到故障前某一时刻的正确状态
登记日志文件

为保证数据库是可恢复的,登记日志文件时必须遵循两条原则

  • 登记的次序严格按并发事务执行的时间次序
  • 必须先写日志文件,后写数据库
    • 写日志文件操作:把表示这个修改的日志记录写到日志文件中
    • 写数据库操作:把对数据的修改写到数据库中

为什么要先写日志文件
写数据库和写日志文件是两个不同的操作
在这两个操作之间可能发生故障

  • 如果先写了数据库修改,而在日志文件中没有登记下这个修改,则以后就无法恢复这个修改了
  • 如果先写日志,但没有修改数据库,按日志文件恢复时只不过是多执行一次不必要的UNDO操作,并不会影响数据库的正确性

恢复策略

事务故障恢复

事务故障:事务在运行至正常终止点前被终止
恢复方法
由恢复子系统利用日志文件撤消(UNDO)此事务已对数据库进行的修改
事务故障的恢复由系统自动完成,对用户是透明的,不需要用户干预

恢复步骤:
反向扫描文件日志(即从最后向前扫描日志文件),查找该事务的更新操作。
对该事务的更新操作执行逆操作。即将日志记录中“更新前的值” 写入数据库。
插入操作, “更新前的值”为空,则相当于做删除操作
删除操作,“更新后的值”为空,则相当于做插入操作
若是修改操作,则相当于用修改前值代替修改后值
继续反向扫描日志文件,查找该事务的其他更新操作,并做同样处理。
如此处理下去,直至读到此事务的开始标记,事务故障恢复就完成了。

系统故障恢复

系统故障造成数据库不一致状态的原因
未完成事务对数据库的更新可能已写入数据库
已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库
恢复方法

  • Undo 故障发生时未完成的事务
  • Redo 已完成的事务

系统故障的恢复由系统在重新启动时自动完成,不需要用户干预

恢复步骤:

正向扫描日志文件(即从头扫描日志文件)
重做(REDO) 队列: 在故障发生前已经提交的事务
这些事务既有BEGIN TRANSACTION记录,也有COMMIT记录
撤销 (UNDO)队列:故障发生时尚未完成的事务
这些事务只有BEGIN TRANSACTION记录,无相应的COMMIT记录

对撤销(UNDO)队列事务进行撤销(UNDO)处理
反向扫描日志文件,对每个撤销事务的更新操作执行逆操作
即将日志记录中“更新前的值”写入数据库
对重做(REDO)队列事务进行重做(REDO)处理
正向扫描日志文件,对每个重做事务重新执行登记的操作
即将日志记录中“更新后的值”写入数据库

介质故障恢复

介质故障的恢复的工作

  • 重装数据库
  • 重做已完成的事务

介质故障的恢复需要数据库管理员介入
数据库管理员的工作

  • 重装最近转储的数据库副本和有关的各日志文件副本
  • 执行系统提供的恢复命令

具体的恢复操作仍由数据库管理系统完成

恢复步骤:
装入最新的后备数据库副本(离故障发生时刻最近的转储副本) ,使数据库恢复到最近一次转储时的一致性状态。
对于静态转储的数据库副本,装入后数据库即处于一致性状态
对于动态转储的数据库副本,还须同时装入转储时刻的日志文件副本,利用恢复系统故障的方法(即REDO+UNDO),才能将数据库恢复到一致性状态。
装入有关的日志文件副本(转储结束时刻的日志文件副本) ,重做已完成的事务。
首先扫描日志文件,找出故障发生时已提交的事务的标识,将其记入重做队列。
然后正向扫描日志文件,对重做队列中的所有事务进行重做处理。即将日志记录中“更新后的值”写入数据库。

具有检查点恢复技术

恢复的两个问题
搜索整个日志将耗费大量的时间
重做处理:重新执行,浪费了大量时间

具有检查点(checkpoint)的恢复技术

  • 在日志文件中增加检查点记录(checkpoint)
  • 增加重新开始文件
  • 恢复子系统在登录日志文件期间动态地维护日志

检查点技术

检查点记录的内容

  • 建立检查点时刻所有正在执行的事务清单
  • 这些事务最近一个日志记录的地址

重新开始文件的内容

  • 记录各个检查点记录在日志文件中的地址

动态维护日志文件

动态维护日志文件的方法
周期性地执行如下操作:建立检查点,保存数据库状态。
具体步骤是:

  • 将当前日志缓冲区中的所有日志记录写入磁盘的日志文件上
  • 在日志文件中写入一个检查点记录
  • 将当前数据缓冲区的所有数据记录写入磁盘的数据库中
  • 把检查点记录在日志文件中的地址写入一个重新开始文件

建立检查点

恢复子系统可以定期或不定期地建立检查点,保存数据库状态

  • 定期
    • 按照预定的一个时间间隔,如每隔一小时建立一个检查点
  • 不定期
    • 按照某种规则,如日志文件已写满一半建立一个检查点

利用检查点恢复策略

使用检查点方法可以改善恢复效率
当事务T在一个检查点之前提交,T对数据库所做的修改已写入数据库
写入时间是在这个检查点建立之前或在这个检查点建立之时
在进行恢复处理时,没有必要对事务T执行重做操作

数据库镜像

介质故障是对系统影响最为严重的一种故障,严重影响数据库的可用性
介质故障恢复比较费时
为预防介质故障,数据库管理员必须周期性地转储数据库
提高数据库可用性的解决方案
数据库镜像(Mirror)

数据库镜像

  • 数据库管理系统自动把整个数据库或其中的关键数据复制到另一个磁盘上
  • 数据库管理系统自动保证镜像数据与主数据的一致性
  • 每当主数据库更新时,数据库管理系统自动把更新后的数据复制过去

用途:
出现介质故障时

  • 可由镜像磁盘继续提供使用
  • 同时数据库管理系统自动利用镜像磁盘数据进行数据库的恢复
  • 不需要关闭系统和重装数据库副本

没有出现故障时

  • 可用于并发操作
  • 一个用户对数据加排他锁修改数据,其他用户可以读镜像数据库上的数据,而不必等待该用户释放锁

频繁地复制数据自然会降低系统运行效率

  • 在实际应用中用户往往只选择对关键数据和日志文件镜像
  • 不是对整个数据库进行镜像

Part10——数据库并发

内容不多,了解即可,而且与操作系统并发其实差别不大

并发控制

事务并发

多用户数据库系统,允许多个用户同时使用的数据库系统
飞机定票数据库系统
银行数据库系统
特点:在同一时刻并发运行的事务数可达数百上千个
事务并发执行带来的问题
会产生多个事务同时存取同一数据的情况
可能会存取和存储不正确的数据,破坏事务隔离性和数据库的一致性
数据库管理系统必须提供并发控制机制,并发控制机制是衡量一个数据库管理系统性能的重要标志之一

多事务执行方式

事务串行执行

  • 每个时刻只有一个事务运行,其他事务必须等到这个事务结束以后方能运行
  • 不能充分利用系统资源,发挥数据库共享资源的特点

交叉并发方式(Interleaved Concurrency)

  • 在单处理机系统中,事务的并行执行是这些并行事务的并行操作轮流交叉运行
  • 单处理机系统中的并行事务并没有真正地并行运行,但能够减少处理机的空闲时间,提高系统的效率

同时并发方式(simultaneous concurrency)

  • 多处理机系统中,每个处理机可以运行一个事务,多个处理机可以同时运行多个事务,实现多个事务真正的并行运行
  • 最理想的并发方式,但受制于硬件环境
  • 更复杂的并发方式机制

并发控制

事务是并发控制的基本单位
并发控制机制的任务

  • 对并发操作进行正确调度
  • 保证事务的隔离性
  • 保证数据库的一致性
不一致例子

老生常谈飞机票

[例11.1]飞机订票系统中的一个活动序列
甲售票点(事务T1)读出某航班的机票余额A,设A=16;
乙售票点(事务T2)读出同一航班的机票余额A,也为16;
甲售票点卖出一张机票,修改余额A←A-1,所以A为15,把A写回数据库;
乙售票点也卖出一张机票,修改余额A←A-1,所以A为15,把A写回数据库
结果明明卖出两张机票,数据库中机票余额只减少1

这种情况称为数据库的不一致性,是由并发操作引起的。
在并发操作情况下,对T1、T2两个事务的操作序列的调度是随机的。
若按上面的调度序列执行, T1事务的修改就被丢失。
原因:第4步中T2事务修改A并写回后覆盖了T1事务的修改

并发操作带来的数据不一致性:
丢失修改(Lost Update)
不可重复读(Non-repeatable Read)
读“脏”数据(Dirty Read)
记号

  • R(x):读数据x
  • W(x):写数据x
丢失修改

两个事务T1和T2读入同一数据并修改, T2的提交结果破坏了T1提交的结果,导致T1的修改被丢失。
上面飞机订票例子就属此类

不可重复读

不可重复读是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。
不可重复读包括三种情况, 后两种不可重复读有时也称为幻影现象(Phantom Row):

  • 事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时,得到与前一次不同的值
  • 事务T1按一定条件从数据库中读取了某些数据记录后,事务T2删除了其中部分记录,当T1再次按相同条件读取数据时,发现某些记录神秘地消失了。
  • 事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。

T1读取B=100进行运算
T2读取同一数据B,对其进行修改后将B=200写回数据库。
T1为了对读取值校对重读B,B已为200,与第一次读取值不一致

读“脏”数据

读“脏”数据是指:
事务T1修改某一数据,并将其写回磁盘
事务T2读取同一数据后, T1由于某种原因被撤销
这时T1已修改过的数据恢复原值, T2读到的数据就与数据库中的数据不一致
T2读到的数据就为“脏”数据,即不正确的数据

T1将C值修改为200, T2读到C为200
T1由于某种原因撤销,其修改作废,C恢复原值100
这时T2读到的C为200,与数据库内容不一致,就是“脏”数据

数据不一致性及并发控制

数据不一致性:由于并发操作破坏了事务的隔离性
并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其他事务的干扰,从而避免造成数据的不一致性
对数据库的应用有时允许某些不一致性,例如有些统计工作涉及数据量很大,读到一些“脏”数据对统计精度没什么影响,可以降低对一致性的要求以减少系统开销

并发控制主要技术
  • 封锁(Locking)
  • 时间戳(Timestamp)
  • 乐观控制法
  • 多版本并发控制(MVCC)

下面也将对这些技术进行了解

封锁

封锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁
加锁后事务T就对该数据对象有了一定的控制,在事务T释放它的锁之前,其它的事务不能更新此数据对象。
封锁是实现并发控制的一个非常重要的技术
一个事务对某个数据对象加锁后究竟拥有什么样的控制由封锁的类型决定。
基本封锁类型
排它锁(Exclusive Locks,简记为X锁)
共享锁(Share Locks,简记为S锁)

排他锁

排它锁又称为写锁
若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁
保证其他事务在T释放A上的锁之前不能再读取和修改A

共享锁

共享锁又称为读锁
若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其它事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁
保证其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlZwrMPr-1687783553580)(null)]

封锁协议

在运用X锁和S锁对数据对象加锁时,需要约定一些规则,这些规则为封锁协议(Locking Protocol)。

  • 何时申请X锁或S锁
  • 持锁时间
  • 何时释放

对封锁方式规定不同的规则,就形成了各种不同的封锁协议,它们分别在不同的程度上为并发操作的正确调度提供一定的保证。

一级封锁协议
  • 一级封锁协议
    • 事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。
      • 正常结束(COMMIT)
      • 非正常结束(ROLLBACK)
  • 一级封锁协议可防止丢失修改,并保证事务T是可恢复的。
  • 在一级封锁协议中,如果仅仅是读数据不对其进行修改,是不需要加锁的,所以它不能保证可重复读和不读“脏”数据。
二级封锁协议
  • 二级封锁协议
    • 一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后即可释放S锁。
  • 二级封锁协议可以防止丢失修改和读“脏”数据。
  • 在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能保证可重复读
三级封锁协议
  • 三级封锁协议
    • 一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放。
  • 三级封锁协议可防止丢失修改、读脏数据和不可重复读。
封锁协议小结

三级协议的主要区别

  • 什么操作需要申请封锁以及何时释放锁(即持锁时间)
    不同的封锁协议使事务达到的一致性级别不同
  • 封锁协议级别越高,一致性程度越高

活锁

事务T1封锁了数据R
事务T2又请求封锁R,于是T2等待。
T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。
T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求……
T2有可能永远等待,这就是活锁的情形
避免活锁:采用先来先服务的策略
当多个事务请求封锁同一数据对象时
按请求封锁的先后次序对这些事务排队
该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁
可怜的T2

死锁

事务T1封锁了数据R1
T2封锁了数据R2
T1又请求封锁R2 ,因T2已封锁了R2 ,于是T1等待T2释放R2上的锁
接着T2又申请封锁R1 ,因T1已封锁了R1 , T2也只能等待T1释放R1上的锁
这样T1在等待T2 ,而T2又在等待T1 , T1和T2两个事务永远不能结束,形成死锁
可怜的T1、T2

预防死锁

产生死锁的原因是两个或多个事务都已封锁了一些数据对象,然后又都请求对已为其他事务封锁的数据对象加锁,从而出现死等待。
预防死锁的发生就是要破坏产生死锁的条件

一次封锁法,要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行
存在的问题
降低系统并发度
难于事先精确确定封锁对象
数据库中数据是不断变化的,原来不要求封锁的数据,在执行过程中可能会变成封锁对象,所以很难事先精确地确定每个事务所要封锁的数据对象。
解决方法:将事务在执行过程中可能要封锁的数据对象全部加锁,这就进一步降低了并发度。

顺序封锁法,预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。
存在的问题
维护成本
数据库系统中封锁的数据对象极多,并且随数据的插入、删除等操作而不断地变化,要维护这样的资源的封锁顺序非常困难,成本很高。
难以实现
事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁

感觉不如PV操作…

死锁诊断

数据库管理系统在解决死锁的问题上更普遍采用的是诊断并解除死锁的方法
在操作系统中广为采用的预防死锁的策略并不太适合数据库的特点

超时法,如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
优点
实现简单
缺点
有可能误判死锁
时限若设置得太长,死锁发生后不能及时发现

等待图法,并发控制子系统周期性地(比如每隔数秒)生成事务等待图,检测事务。如果发现图中存在回路,则表示系统中出现了死锁。
事务等待图是一个有向图G=(T,U)
T为结点的集合,每个结点表示正运行的事务
U为边的集合,每条边表示事务等待的情况
若T1等待T2 ,则T1 , T2之间划一条有向边,从T1指向T2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ffkwHq7R-1687783561393)(null)]

死锁解除

选择一个处理死锁代价最小的事务,将其撤消
释放此事务持有的所有的锁,使其它事务能继续运行下去

事务调度

可串行

并发调度的可串行性

数据库管理系统对并发事务不同的调度可能会产生不同的结果
串行调度是正确的
执行结果等价于串行调度的调度也是正确的,称为可串行化调度

可串行化调度

可串行化(Serializable)调度
多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行地执行这些事务时的结果相同
可串行性(Serializability)
是并发事务正确调度的准则
一个给定的并发调度,当且仅当它是可串行化的,才认为是正确调度

只要与某一串行调度结果一致即可,判断是否为可串行化调度方法:

  • 列出所有串行化调度结果
  • 比较并行调度结果是否符合上述穿行结果中的某一种
  • 相同则为正确调度,否则为错误调度
冲突可串行化

冲突可串行化

  • 一个比可串行化更严格的条件
  • 商用系统中的调度器采用

冲突操作:是指不同的事务对同一数据的读写操作和写写操作:

  • Ri(x)与Wj(x) /事务Ti读x,Tj写x,其中i≠j/
  • Wi(x)与Wj(x) /事务Ti写x,Tj写x,其中i≠j/

其他操作是不冲突操作
不能交换(Swap)的动作:

  • 同一事务的两个操作
  • 不同事务的冲突操作

一个调度Sc在保证冲突操作的次序不变的情况下,通过交换两个事务不冲突操作的次序得到另一个调度Sc’,如果Sc’是串行的,称调度Sc是冲突可串行化的调度
若一个调度是冲突可串行化,则一定是可串行化的调度
可用这种方法判断一个调度是否是冲突可串行化的

冲突可串行化调度是可串行化调度的充分条件,不是必要条件。还有不满足冲突可串行化条件的可串行化调度。

[例11.4]有3个事务,T1=W1(Y)W1(X),T2=W2(Y)W2(X),T3=W3(X)
调度L1=W1(Y)W1(X)W2(Y)W2(X)W3(X)是一个串行调度。
调度L2=W1(Y)W2(Y)W2(X)W1(X)W3(X)不满足冲突可串行化。但是调度L2是可串行化的,因为L2执行的结果与调度L1相同,Y的值都等于T2的值,X的值都等于T3的值

知乎这篇文章介绍了一种画图判断是否冲突可串行化的方法,十分适用

两段锁协议

数据库管理系统普遍采用两段锁协议的方法实现并发调度的可串行性,从而保证调度的正确性
两段锁协议,指所有事务必须分两个阶段对数据项加锁和解锁

  • 在对任何数据进行读、写操作之前,事务首先要获得对该数据的封锁
  • 在释放一个封锁之后,事务不再申请和获得任何其他封锁

“两段”锁的含义,事务分为两个阶段

  • 第一阶段是获得封锁,也称为扩展阶段
    • 事务可以申请获得任何数据项上的任何类型的锁,但是不能释放任何锁
  • 第二阶段是释放封锁,也称为收缩阶段
    • 事务可以释放任何数据项上的任何类型的锁,但是不能再申请任何锁

事务遵守两段锁协议是可串行化调度的充分条件,而不是必要条件。
若并发事务都遵守两段锁协议,则对这些事务的任何并发调度策略都是可串行化的
若并发事务的一个调度是可串行化的,不一定所有事务都符合两段锁协议

两段锁协议与防止死锁的一次封锁法

  • 一次封锁法要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行,因此一次封锁法遵守两段锁协议
  • 但是两段锁协议并不要求事务必须一次将所有要使用的数据全部加锁,因此遵守两段锁协议的事务可能发生死锁

封锁粒度

封锁对象的大小称为封锁粒度(Granularity)
封锁的对象:逻辑单元,物理单元
例:在关系数据库中,封锁对象:

  • 逻辑单元: 属性值、属性值的集合、元组、关系、索引项、整个索引、整个数据库等
  • 物理单元:页(数据页或索引页)、物理记录等

封锁粒度与系统的并发度和并发控制的开销密切相关。
封锁的粒度越大,数据库所能够封锁的数据单元就越少,并发度就越小,系统开销也越小;
封锁的粒度越小,并发度较高,但系统开销也就越大

若封锁粒度是数据页,事务T1需要修改元组L1,则T1必须对包含L1的整个数据页A加锁。如果T1对A加锁后事务T2要修改A中元组L2,则T2被迫等待,直到T1释放A。
如果封锁粒度是元组,则T1和T2可以同时对L1和L2加锁,不需要互相等待,提高了系统的并行度。
又如,事务T需要读取整个表,若封锁粒度是元组,T必须对表中的每一个元组加锁,开销极大

多粒度封锁

多粒度封锁(Multiple Granularity Locking)
在一个系统中同时支持多种封锁粒度供不同的事务选择
选择封锁粒度,同时考虑封锁开销和并发度两个因素, 适当选择封锁粒度
需要处理多个关系的大量元组的用户事务:以数据库为封锁单位
需要处理大量元组的用户事务:以关系为封锁单元
只处理少量元组的用户事务:以元组为封锁单位

多粒度树

多粒度树
以树形结构来表示多级封锁粒度
根结点是整个数据库,表示最大的数据粒度
叶结点表示最小的数据粒度
例:三级粒度树。根结点为数据库,数据库的子结点为关系,关系的子结点为元组。

多粒度封锁协议

允许多粒度树中的每个结点被独立地加锁
对一个结点加锁意味着这个结点的所有后裔结点也被加以同样类型的锁
在多粒度封锁中一个数据对象可能以两种方式封锁:
显式封锁: 直接加到数据对象上的封锁
隐式封锁:是该数据对象没有独立加锁,是由于其上级结点加锁而使该数据对象加上了锁
显式封锁和隐式封锁的效果是一样的

显式封锁和隐式封锁

系统检查封锁冲突时,要检查显式封锁,还要检查隐式封锁,上级下级都要检查
例如,事务T要对关系R1加X锁
系统必须搜索其上级结点数据库、关系R1
还要搜索R1的下级结点,即R1中的每一个元组
如果其中某一个数据对象已经加了不相容锁,则T必须等待

对某个数据对象加锁,系统要检查

  • 该数据对象
    • 有无显式封锁与之冲突
  • 所有上级结点
    • 检查本事务的显式封锁是否与该数据对象上的隐式封锁冲突:(由上级结点已加的封锁造成的)
  • 所有下级结点
    • 看上面的显式封锁是否与本事务的隐式封锁(将加到下级结点的封锁)冲突

意向锁

引进意向锁(intention lock)目的

  • 提高对某个数据对象加锁时系统的检查效率

如果对一个结点加意向锁,则说明该结点的下层结点正在被加锁
对任一结点加基本锁,必须先对它的上层结点加意向锁
例如,对任一元组加锁时,必须先对它所在的数据库和关系加意向锁

意向共享锁

意向共享锁(Intent Share Lock,简称IS锁)
如果对一个数据对象加IS锁,表示它的后裔结点拟(意向)加S锁。
例如:事务T1要对R1中某个元组加S锁,则要首先对关系R1和数据库加IS锁

意向排他锁

意向排它锁(Intent Exclusive Lock,简称IX锁)
如果对一个数据对象加IX锁,表示它的后裔结点拟(意向)加X锁。
例如:事务T1要对R1中某个元组加X锁,则要首先对关系R1和数据库加IX锁

共享意向排它锁

共享意向排它锁(Share Intent Exclusive Lock,简称SIX锁)
如果对一个数据对象加SIX锁,表示对它加S锁,再加IX锁,即SIX = S + IX。
例:对某个表加SIX锁,则表示该事务要读整个表(所以要对该表加S锁),同时会更新个别元组(所以要对该表加IX锁)。

锁的强度

锁的强度是指它对其他锁的排斥程度
一个事务在申请封锁时以强锁代替弱锁是安全的,反之则不然

具有意向锁的多粒度封锁方法

申请封锁时应该按自上而下的次序进行
释放封锁时则应该按自下而上的次序进行
具有意向锁的多粒度封锁方法

  • 提高了系统的并发度
  • 减少了加锁和解锁的开销
  • 在实际的数据库管理系统产品中得到广泛应用

例如:事务T1要对关系R1加S锁
要首先对数据库加IS锁
检查数据库和R1是否已加了不相容的锁(X或IX)
不再需要搜索和检查R1中的元组是否加了不相容的锁(X锁)

确实精妙

Part11——NoSQL

总算来到了我们的终点站,也是最麻烦的,毕竟PPT是真的多。前面我们都是围绕关系型数据库来看的,现在来看看一种特殊的数据库NoSQL

关系型数据库的价值

获取持久化数据

持久存储大量数据
在大多数的计算架构中,有两个存储区域:
速度快但是数据易丢失的“主存储器”(main memory)
空间有限
易挥发
存储量大但速度较慢的“后备存储器”(backing store)
文件系统,如许多生产力应用程序(productivity application,比如文字处理软件)
数据库,大多数企业级应用程序

并发

多个用户会一起访问同一份数据体,并且可能要修改这份数据。(大多数情况下,他们都在不同数据区域内各自操作,但是,偶尔也会同时操作一小块数据)
关系型数据库提供了 “事务”机制来控制对其数据的访问,以便处理此问题。
事务在处理错误时也有用。通过事务更改数据时,如果在处理变更的过程中出错了,那么就可以回滚(roll back)这一事务,以保证数据不受破坏。

集成

企业级应用程序居于一个丰富的生态系统中,它需要与其他应用程序协同工作。不同的应用程序经常要使用同一份数据,而且某个应用程序更新完数据之后,必须让其他应用程序知道这份数据已经改变了。
常用的办法是使用共享数据库集成(shared database integration) ,多个应用程序都将数据保存在同一个数据库中。这样一来,所有应用程序很容易就能使用彼此的数据了。
与多用户访问单一应用程序时一样,数据库的并发控制机制也可以应对多个应用程序。

近乎标准的模型

关系型数据库以近乎标准的方式提供了数据模型。
尽管各种关系型数据库之间仍有差异,但其核心机制相同

  • 不同厂商的SQL方言相似
  • “事务” 的操作方式也几乎一样

NoSQL由来

阻抗失谐

基于关系代数(relational algebra),关系模型把数据组织成 “关系”(relation)和“元组”(tuple)。
元组是由“键值对”(name-value pair)构成的集合
而关系则是元组的集合。
SQL操作所使用及返回的数据都是“关系”
元组不能包含“嵌套记录”(nested record)或“列表”(list) 等任何结构
而内存中的数据结构则无此限制,它可以使用的数据组织形式比“关系”更丰富。
关系模型和内存中的数据结构之间存在差异。这种现象通常称为“阻抗失谐”。
如果在内存中使用了较为丰富的数据结构,那么要把它保存到磁盘之前,必须先将其转换成“关系形式。于是就发生了“阻抗失谐”:需要在两种不同的表示形式之间转译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AtoqznS1-1687783549468)(null)]

确实,看着清晰的数据内容却被局限在一张张表格之中,有时候看起来更不明晰

解决方法

面向对象数据库
“对象-关系映射框架”( object-relational mapping framework) 通过映射模式( mapping pattern)表达转换
问题:

  • 查询性能问题
  • 集成问题

集成数据库

SQL充当了应用程序之间的一种集成机制。数据库在这种情况下成了“集成数据库”(integration database)
通常由不同团队所开发的多个应用程序,将其数据存储在一个公用的数据库中。
所有应用程序都在操作内容一致的持久数据,提高了数据通信的效率
为了能将很多应用程序集成起来,数据库的结构比单个应用程序所要用到的结构复杂得多
如果某个应用程序想要修改存储的数据,那么它就得和所有使用此数据库的其他应用程序相协调。
各种应用程序的结构和性能要求不尽相同,数据库通常不能任由应用程序更新其数据。为了保持数据库的完整性,我们需要将这一责任交由数据库自身负责。

应用程序数据库

将数据库视为“应用程序数据库”(application database), 其内容只能由一个应用程序的代码库直接访问

  • 由于只有开发应用程序的团队才需要知道其结构,模式的维护与更新就更容易了。由于应用程序开发团队同时管理数据库和应用程序代码,因此可以把维护数据库完整性的工作放在应用程序代码中。

交互工作转交由应用程序接口来完成

  • “面向服务架构” 、Web服务。使得应用程序间通过平台中立的方式完成集成。
  • 在Web服务作为集成机制后,所交换的数据可以拥有更为灵活的结构
    • 如XML、 JSON格式,它们均能够使用嵌套记录及列表等更丰富的数据结构
    • 使用“面向文档”的交互方式,减少通讯次数和开销
    • 既可以传输文本,也可以传输二进制

在使用应用程序数据库后,由于内部数据库与外部通信服务之间已经解耦,所以外界并不关心数据如何存储,这样就可以选用非关系型数据库了

  • 关系型数据库的许多特性,诸如安全性等,可以交给使用该数据库的外围应用程序(enclosing application)来做

集群问题

纵向扩展(scale up)及横向扩展(scale out)

  • 采用集群应对横向扩展

关系型数据库的“分片”和“复制”

  • 在负载分散的同时,应用程序必须控制所有分片,需要知道数据库中的每份小数据的存储情况
  • 如何确保跨分片的查询、参照完整性(referential integrity)、 事务、一致性控制(consistency control)等操作

NoSQL

NoSQL没有规范的定义

  • “开源分布式的非关系型数据库”

各种NoSQL数据库的共同特性是

  • 不使用关系模型
  • 在集群中运行良好
    • 关系型数据库使用ACID事务来保持整个数据库的一致性,而这种方式本身与集群环境相冲突
    • NoSQL数据库为处理并发及分布问题提供了众多选项。
  • 开源
  • 适用于21世纪的互联网公司
  • 无模式
    • 不用事先修改结构定义,即可自由添加字段了
    • 这在处理不规则数据和自定义字段时非常有用

聚合

把一组相互关联的对象视为一个整体单元来操作,而这个单元就叫聚合(aggregate)。

  • 通过原子操作(atomic operation)更新聚合的值(含一致性管理)
  • 以聚合为单位,进行数据存储通信
  • 在集群中操作数据库时,用聚合为单位来复制和分片
  • 聚合描述数据访问方式

面向聚合操作数据时所用的单元,其结构比元组集合复杂得多

  • “键值数据库”、“文档数据库”、“列族数据库”

聚合无知

关系型数据库的数据模型中,没有“聚合”这一概念,因此我们称之为“聚合无知”(aggregate-ignorant)。
“图数据库"也是聚合无知的。
聚合反应数据操作的边界,很难在共享数据的多个场景中“正确” 划分,对某些数据交互有用的聚合结构,可能会阻碍另一些数据交互
在客户下单并核查订单,以及零售商处理订单时,将订单视为一个聚合结构就比较合适。
如零售商要分析过去几个月的产品销售情况,如果将订单做成一个聚合,就必须深挖数据库中的每一个聚合。
若是采用“聚合无知模型”,如果没有一种占主导地位的结构,那么很容易就能以不同方式来查看数据

聚合之间的关系

例如:把订单和客户放在两个聚合中,但是想在它们之间设定某种关系,以便能根据订单查出客户数据

  • 要提供这种关联,最简单的办法就是把客户ID嵌入订单的聚合数据中。在应用层级提供关联
  • 在数据库层级提供聚合之间关系的表达机制

操作多个有关联的聚合,由应用保证其正确性

  • 面向聚合数据库获取数据时以聚合为单元,只能保证单一聚合内部内容的原子性

聚合、集群和事务处理

在集群上运行时,需要把采集数据时所需的节点数降至最小

  • 如果在数据库中明确包含聚合结构,那么它就可以根据这一重要信息,知道哪些数据需要一起操作了,而且这些数据应该放在同一个节点中

通常情况下,面向聚合的数据库不支持跨越多个聚合的ACID事务。它每次只能在一个聚合结构上执行原子操作。

  • 如果想以原子方式操作多个聚合,那么就必须自己组织应用程序的代码
  • 在实际应用中,大多数原子操作都可以局限于某个聚合结构内部,而且,在将数据划分为聚合时,这也是要考虑的因素之一

主要NoSQL数据模型

键值数据模型与文档数据模型

这两类数据库都包含大量聚合,每个聚合中都有一个获取数据所用的键或ID。
两种模型的区别是:
键值数据库的聚合不透明,只包含语义中立的大块信息
数据库可能会限制聚合的总大小,但除此之外,聚合中可以存储任意数据。
在键值数据库中,要访问聚合内容,只能通过键来查找
在文档数据库的聚合中,可以看到其结构。
限制其中存放的内容,它定义了其允许的结构与数据类型
能够更加灵活地访问数据。通过用聚合中的字段查询,可以只获取一部分聚合,而不用获取全部内容
可以按照聚合内容创建索引

列族存储

大部分数据库都以行为单元存储数据。然而,有些情况下写入操作执行得很少,但是经常需要一次读取若干行中的很多列。此时,列存储数据库将所有行的某一组列作为基本数据存储单元
列族数据库将列组织为列族。每一列都必须是某个列族的一部分,而且访问数据的单元也得是列
某个列族中的数据经常需要一起访问

列族模型将其视为两级聚合结构(two-level aggregate structure)。
第一个键通常代表行标识符,与“键值存储”相同,可以用它来获取想要的聚合
列族结构与“键值存储”的区别在于,其“行聚合”(row aggregate)本身又是一个映射,其中包含一些更为详细的值。这些“二级值” (second-level value)就叫做“列”。与整体访问某行数据一样,我们也可以操作特定的列

两种数据组织方式
面向行(row-oriented):每一行都是一个聚合(例如ID为1234的顾客就是一个聚合),该聚合内部存有一些包含有用数据块(客户信息、订单记录)的列族
面向列(column-oriented): 每个列族都定义了一种记录类型(例如客户信息),其中每行都表示一条记录。数据库中的大“行”理解为列族中每一个短行记录的串接

面向聚合的数据模型

共同点
都使用聚合这一概念,而且聚合中都有一个可以查找其内容的索引键
在集群上运行时,聚合是中心环节,因为数据库必须保证将聚合内的数据存放在同一个节点上
聚合是“更新”操作的最小数据单位(atomic unit),对事务控制来说,以聚合为操作单元
差别
键值数据模型将聚合看作不透明的整体,只能根据键来查出整个聚合,而不能仅仅查询或获取其中的一部分
文档模型的聚合对数据库透明,于是就可以只查询并获取其中一部分数据了,不过,由于文档没有模式,因此在想优化存储并获取聚合中的部分内容时,数据库不太好调整文档结构
列族模型把聚合分为列族,让数据库将其视为行聚合内的一个数据单元。此类聚合的结构有某种限制,但是数据库可利用此种结构的优点来提高其易访问性。

图数据库

图数据库的基本数据模型:由边(或称“弧”,arc)连接而成的若干节点。
可以用专门为“图”而设计的查询操作来搜寻图数据库的网络了
指定节点,通过边进行查询
关系型数据可以通过“外键”实现,查询中的多次连接,效率较差

无模式

关系型数据库中,首先必须定义“模式”,然后才能存放数据。
NoSQL数据库,无模式:
“键值数据库”可以把任何数据存放在一个“键”的名下。
“文档数据库” 对所存储的文档结构没有限制
在列族数据库中,任意列里面都可以随意存放数据
图数据库中可以新增边,也可以随意向节点和边中添加属性。

处理格式不一致的数据

每条记录都拥有不同字段集(set of field)
关系型数据库中,“模式”会将表内每一行的数据类型强行统一,若不同行所存放的数据类型不同,那这么做就很别扭。
要么得分别用很多列来存放这些数据,而且把用不到的字段值填成null(这就成了“稀疏表”,sparse table),
要么就要使用类似custom column 4这样没有意义的列类型。
无模式表则没有这么麻烦,每条记录只要包含其需要的数据即可,不用再担心上面的问题了。

无模式的问题

存在“隐含模式”。在编写数据操作代码时,对数据结构所做的一系列假设
应用与数据的耦合问题
无法在数据库层级优化和验证数据
在集成数据库中,很难解决
使用应用程序数据库,并使用Web Services、SOA等完成集成
在聚合中为不同应用程序明确划分出不同区域
在文档数据库中,可以把文档分成不同的区段(section)
在列族数据库,可以把不同的列族分给不同的应用程序

分布式模型

数据分布

数据分布有两条路径:分片(sharding)与复制(replication) 。

  • “分片”则是将不同数据存放在不同节点中
  • “复制”就是将同一份数据拷贝至多个节点;
    • “主从式”(master-slave)和“对等式”(peer-to-peer)

既可以在两者中选一个来用,也可以同时使用它们。

前面看到这两概念一脸懵逼…

单一服务器

最简单的分布形式:根本不分布。

  • 将数据库放在一个节点中,让它处理对数据存储的读取与写入操作。
  • 不用考虑使用其他方案时所需应对的复杂事务,这对数据操作管理者与应用程序开发者来说,都比较简单。

尽管许多NoSQL数据库都是为集群运行环境而设计的,但是只要符合应用程序需求,那就完全可以按照单一服务器的分布模型来使用

  • 图数据库配置在一台服务器上
  • 如果只是为了处理聚合,那么可以考虑在单一服务器上部署“文档数据库”或“键值数据库”
分片

一般来说,数据库的繁忙体现在:不同用户需要访问数据集中的不同部分。
在这种情况下,把数据的各个部分存放于不同的服务器中,以此实现横向扩展。该技术就叫“分片”(sharding)。

在理想情况下,不同的服务器节点会服务于不同的用户。每位用户只需与一台服务器通信,并且很快就能获得服务器的响应。网络负载相当均衡地分布于各台服务器上。

为达成目标,必须保证需要同时访问的那些数据都存放在同一节点上,而且节点必须排布好这些数据块,使访问速度最优。

  • 若使用面向聚合的数据库,可以把聚合作为分布数据的单元。
  • 在节点的数据排布问题上,有若干个与性能改善相关的因素。
    • 地理因素
    • 负载均衡
    • 聚合有序放置

采用应用程序的逻辑实现分片

  • 编程模型复杂化,因为应用程序的代码必须负责把查询操作分布到多个分片上
  • 若想重新调整分片,那么既要修改程序代码,又要迁移数据

采用NoSQL数据库提供的“自动分片”(auto-sharding)功能

  • 让数据库自己负责把数据分布到各分片
  • 并且将数据访问请求引导至适当的分片上

分片可以同时提升读取与写入效率
使用“复制”技术,尤其是带缓存的复制,可以极大地改善读取性能,但对于写操作帮助不大
分片对改善数据库的“故障恢复能力”帮助并不大。尽管数据分布在不同的节点上,但是和“单一服务器”方案一样,只要某节点出错,那么该分片上的数据就无法访问了
在发生故障时,只有访问此数据的那些用户才会受影响,而其余用户则能正常访问
由于多节点问题,从实际效果出发,分片技术可能会降低数据库的错误恢复能力

主从复制

在“主从式分布”(master-slave distribution)中
其中有一个节点叫做“主(master) 节点”,或“主要(primary) 节点”。主节点存放权威数据,而且通常负责处理数据更新操作。
其余节点都叫“从(slave) 节点”,或“次要(secondary) 节点”,和主节点保持同步,负责读取操作 。
在需要频繁读取数据集的情况下,“主从复制”(master-slave replication) 有助于提升数据访问性能
以新增更多从节点的方式来进行水平扩展,就可以同时处理更多数据读取请求,并且能保证将所有请求都引导至从节点
在写入操作特别频繁的场合,数据库仍受制于主节点处理更新,以及向从节点发布更新的能力
“主从复制” 可以增强“读取操作的故障恢复能力”(read resilience)
万一主节点出错了,那么从节点依然可以处理读取请求。
主节点出错之后,除非将其恢复,或另行指派新的主节点,否则数据库就无法处理写入操作。
在主节点出错之后,由于拥有内容与主节点相同的从节点,很快就能指派一个从节点作为新的主节点,从而具备故障恢复能力。
主节点可以手工指派,也可自动选择。
“数据的不一致性”

对等复制

“对等复制” 它没有“主节点”这一概念。所有“副本”(replica) 地位相同,都可以接受写入请求,而且丢失其中一个副本,并不影响整个数据库的访问。

结合“主从复制”与“分片”

如果同时使用“主从复制”与“分片”,那么就意味着整个系统有多个主节点,然而对每项数据来说,负责它的主节点只有一个
根据配置需要,同一个节点既可以做某些数据的主节点,也可以充当其他数据的从节点,此外,也可以指派全职的主节点或从节点

结合“对等复制”与“分片”

使用列族数据库时,经常会将“对等复制”与“分片”结合起来。
数据可能分布于集群中的数十个或数百个节点上。在采用“对等复制”方案时,一开始可以用“3”作为复制因子(replication factor), 也就是把每个分片数据放在3个节点中。一旦某个节点出错,那么它上面保存的那些分片数据会由其他节点重建

分布式模型中的一致性

写入冲突和读写冲突

当两个客户端试图同时修改一份数据时,会发生“写入冲突”。而当某客户端在另一个客户端执行写入操作的过程中读取数据时,则会发生“读写冲突”。
悲观方式以锁定数据记录来避免冲突
“写入锁" (write lock)
乐观方式则在事后检测冲突并将其修复
“条件更新”( conditional update),任意客户在执行更新操作之前,都要先测试数据的当前值和其上一次读入的值是否相同
保存冲突数据 。用户自行“合并”(merge)或 “自动合并”(面向特定领域)

NoSQL的不一致性

“图数据库”常常和关系型数据库一样,也支持ACID事务。
面向聚合的数据库通常支持“原子更新”( atomic update),但仅限于单一聚合内部
“一致性” 可以在某个聚合内部保持,但在各聚合之间则不行
在执行影响多个聚合的更新操作时,会留下一段时间空档,让客户端有可能在此刻读出逻辑不一致的数据
存在不一致风险的时间长度就叫“不一致窗口”(inconsistency window)。

一致性

“复制一致性”(replication consistency)。要求从不同副本中读取同一个数据项时,所得到的值相同

在分布式系统中,如果某些节点收到了更新数据,而另外一些节点却尚未收到,那么这种情况就视为“读写冲突”。
若写入操作已经传播至所有节点,则此刻的数据库就具备“最终一致性”( eventually consistent)
复制不一致性带来的“不一致窗口”,在考虑网络环境后,会比单一节点导致的“不一致窗口”长的多

  • 不一致性窗口对应用的影响不同

“照原样读出所写内容的一致性”(read-your-writes consistency) ,在执行完更新操作之后,要能够立刻看到新值。
在具备“最终一致性” 的系统中,可以提供“会话一致性”( session consistency) :在用户会话内部保持“照原样读出所写内容的一致性”
使用“黏性会话”(sticky session),即绑定到某个节点的会话(这种性质也叫做“会话亲和力”,session affinity)。
“黏性会话”可以保证,只要某节点具备“照原样读出所写内容的一致性”,那么与之绑定的会话就都具备这种特性了。
“黏性会话”的缺点是,它会降低“负载均衡器”( load balancer)的效能
使用“版本戳”(version stamp),并确保同数据库的每次交互操作中,都包含会话所见的最新版本戳。服务器节点在响应请求之前必须先保证,它所含有的更新数据包含此版本戳。

使用“黏性会话”和“主从复制”来保证“会话一致性”时,由于读取与写入操作分别发生在不同节点,那么想保证这一点会比较困难。

  • 方法一:将写入请求先发给从节点,由它负责将其转发至主节点,并同时保持客户端的“会话一致性”。
  • 方法二:在执行写入操作时临时切换到主节点,并且在从节点尚未收到更新数据的这一段时间内,把读取操作都交由主节点来处理。

放宽“一致性”和“持久性”约束

使用事务保障“一致性”

使用“事务”达成强一致性
引入放松“隔离级别” ( isolation level)的功能,以允许查询操作读取尚未提交的数据。
读未提交,一个事务可以读取另一个未提交事务的数据。脏读
读已提交,一个事务要等另一个事务提交后才能读取数据。不可重复读
可重复读,在开始读取数据(事务开启)时,不再允许修改操作。幻读
可串行化,事务串行化顺序执行。严格一致性,效率是一个问题

事务的问题

在并发不大的前提下,是否需要事务
在数据较多的情况下,为了让应用性能符合用户要求,它们必须弃用“事务”
尤其在需要引入分片机制时,更是如此
在分布式应用中,如事务的业务范围涉及多个以网络连接的参与者。其规模、复杂度和波动性均导致无法使用事务进行良好描述

CAP定理

CAP定理:给定“一致性”(Consistency)、“可用性”(Availability)、“分区耐受性”( Partition tolerance) 这三个属性,我们只能同时满足其中两个属性。
“一致性”
“可用性”,如果客户可以同集群中的某个节点通信,那么该节点就必然能够处理读取及写入操作。
“分区耐受性” ,如果发生通信故障,导致整个集群被分割成多个无法互相通信的分区时(这种情况也叫“ 脑裂”,split brain),集群仍然可用。

CA系统

CA系统,也就是具备“一致性”(Consistency)与“可用性”(Availability), 但却不具备“分区耐受性”的系统
大多数关系型数据库
CA集群
无法保证“分区耐受性”,这使得一旦“分区”发生,所有节点必须停止运作
CAP中的,可用性定义为“系统中某个无故障节点所接收的每一条请求, 无论成功或失败,都必将得到响应。”
介于此时所有节点均为故障节点,不违反CAP中的“可用性”

CAP定理的现实含义

尽管“CAP定理”经常表述为“三个属性中只能保有两个”,实际上当系统可能会遭遇“分区”状况时(比如分布式系统),需要在“一致性”与“可用性”之间进行权衡

  • 这并不是个二选一的决定,通常来说,我们都会略微舍弃“一致性”,以获取某种程度的“可用性”
  • 这样的系统,既不具备完美的“一致性”,也不具备完美的“可用性”
  • 但是能够满足需要
缺乏“可用性”的例子:

假设Martin与Pramod都想预订某旅馆的最后一间客房,预订系统使用“对等式分布模型”,它由两个节点组成
Martin 使用位于伦敦的节点,而Pramod使用位于孟买的节点。
若要确保一致性,那么当Martin要通过位于伦敦的节点预订房间时,该节点在确认预订操作之前,必须先告知位于孟买的节点。
两个节点必须按照相互一致的顺序来处理它们所收到的操作请求
此方案保证了“一致性”,但是假如网络连接发生故障,那么由故障导致的两个“分区”系统,就都无法预订旅馆房间了,于是系统失去了“可用性”。

改善“可用性”的例子:
指派其中一个节点作为某家旅馆的“主节点”,确保所有预订操作都由“主节点”来处理。
假设位于孟买的节点是“主节点”,那么在两个节点之间的网络连接发生故障之后,它仍然可以处理该旅馆的房间预订工作,这样Pramod将会订到最后一间客房
位于伦敦的用户看到的房间剩余情况会与孟买不一致,但是他们无法预订客房,于是就出现了“更新不一致”现象。
Martin可以和位于伦敦的节点通信,但是该节点却无法更新数据。于是出现了“可用性”故障(availability failure)
这种在“一致性”与“可用性”之间所做的权衡,能正确处理上述特殊状况。

进一步改善“可用性”的例子:
让两个“分区”系统都接受客房预订请求,即使在发生网络故障时也如此。
这种方案的风险是,Martin和Pramod有可能都订到了最后一间客房。然而,根据这家旅馆的具体运营情况,这也许不会出问题:
通常来说,旅行公司都允许一定数量的超额预订,这样的话,如果有某些客人预订了房间而最终没有人住,那么就可以把这部分空余房间分给那些超额预订的人了
与之相对,某些旅馆总是会在全部订满的名额之外多留出几间客房,这样万一哪间客房出了问题,或者在房间订满之后又来了一位贵宾,那么旅馆可以把客人安排到预留出来的空房中
还有些旅馆甚至选择在发现预订冲突之后向客户致歉并取消此预订。
该方案所付出的代价,要比因为网络故障而彻底无法预订的代价小。

一个写入不一致的例子

购物车是允许“写入不一致”现象的一个经典示例
即使网络有故障,也总是能够修改购物车中的商品。
这么做有可能导致多个购物车出现
而结账过程则会将两个购物车合并,具体做法是,将两个购物车中的每件商品都拿出来,放到另外一个购物车中,并按照新的购物车结账。
这个办法基本上不会出错,万一有问题,客户也有机会在下单之前先检视一下购物车中的东西。

BASE

与关系型数据库所支持的ACID事务不同,NoSQL系统具备“BASE属性”
基本可用,Basically Available
柔性状态,Soft state
最终一致性,Eventual consistency
“ACID”与“BASE"不是非此即彼的关系,两者之间存在着多个逐渐过渡的权衡方案可选。

权衡

“持久性”的权衡
数据库大部分时间都在内存中运行,更新操作也直接写入内存,并且定期将数据变更写回磁盘
可以大大提高响应请求的速度。
代价在于,一旦服务器发生故障,任何尚未写回磁盘的更新数据都将丢失。
多用户的“会话状态”信息
会话数据就算丢失,与应用系统效率相比,也不过是个小麻烦。这时可以考虑非持久性写入操作”(nondurable write)。
可以在每次发出请求时,指定该请求所需的持久性。从而,把某些极为重要的更新操作立刻写回磁盘。
捕获物理设备的遥测数据(telemetric data)。就算最近的更新数据可能会因为服务器发生故障而丢失,也还是选择把快速捕获数据放在首位。

分布模型中“持久性”的权衡
如一个节点处理完更新操作之后,在更新数据尚未复制到其他节点之前就出错了,那么则会发生“复制持久性”(replication durability) 故障。
假设有一个采用“主从式分布模型”的数据库,在其主节点出错时,它会自动指派一个从节点作为新的主节点。
若主节点发生故障,则所有还未复制到其他副本的写入操作就都将丢失
一旦主节点从故障中恢复过来,那么,该节点上的更新数据就会和发生故障这段时间内新产生的那些更新数据相冲突
我们把这视为一个“持久化”问题,因为主节点既然已经接纳了这个更新操作,那么用户自然就会认为该操作已经顺利执行完,但实际上,这份更新数据却因为主节点出错而丢失了
解决方案:

  • 不重新指派新的主节点
    • 在主节点出错之后迅速将其恢复
  • 确保主节点在收到某些副本对更新数据的确认之后,再告知用户它已接纳此更新
    • 从节点发生故障时,集群不可用
    • 拖慢更新速度

与处理“持久性”的基本手段类似,也可以针对单个请求来指定其所需的持久性。

仲裁

写入仲裁

处理请求所的节点越多,避免“不一致”问题的能力就越强,要想保“强一致性”(strong consistency), 需要使用多少个节点才行?

  • “对等式分布模型”:
    • “写入仲裁”(write quorum):如果发生两个相互冲突的写入操作,那么只有其中一个操作能为超过半数的节点所认可,W>N/2 。即,参与写入操作的节点数(W),必须超过副本节点数(N)的一半。副本个数又称为“复制因子”
  • “主从式分布模型”
    • 只需要向主节点中写入数据

读取仲裁

想要保证能够读到最新数据,必须与多少个节点联系才行?

  • “对等式分布模型”:
    • 只有当R+W>N时,才能保证读取操作的“强一致性”。其中,执行读取操作时所需联系的节点数®,确认写入操作时所需征询的节点数(W),以及复制因子(N)
  • “主从式分布模型”
    • 只需从主节点中读取数据

复制因子

复制因子”( replication factor)。
一个集群有100 个节点,然而其“复制因子”可能仅仅是3,因为大部分数据都分布在各个"分片”之中。
将“复制因子”设为3,就可以获得足够好的“故障恢复能力”了。
如果只有一个节点出错,那么仍然能够满足读取与写入所需的最小法定节点数。
若是有自动均衡(automatic rebalancing) 机制,那么用不了多久,集群中就会建立起第三个副本,在替代副本建立好之前,再次发生副本故障的概率很小。

实际情况

需要在“一致性”与“可用性”之间权衡,参与某个操作的节点数,可能会随着该操作的具体情况而改变。
在写入数据时,根据“一致性”与“可用性”这两个因素的重要程度,有一些更新操作可能需要获得足够的节点支持率才能执行,而另外一些则不需要。
与之相似,某些读取操作可能更看中执行速度,而可以容忍过时数据,此时,它就可以少联系几个节点。
通常需要协调考虑读、写两种情况:
假设需要快速且具备“强一致性”的读取操作,那么写入操作就要得到全部节点的确认才行,这样的话,只需联系一个节点,就能完成读取操作了(N=3,W=3,R=1)
但是,这个方案意味着,写入操作会比较慢,因为它们必须得到全部三个节点确认之后,才能执行,而且此时连一个节点都不能出错

版本戳

商业活动

“商业活动”(Business Activity)。
例如,用户浏览产品目录,选中了一瓶价格很实惠的Talisker威士忌,填入信用卡信息,然后确认订单。
需要确保最终一致性,但是出于时间、交互的考虑,无法使用事务加以实现
如使用事务实现,必须锁住数据库中各个元素。而长时间锁定元素是不现实的。
因此,应用程序通常只在处理完用户交互操作之后才开始“系统事务”,这样的话,锁定时间就比较短了。
然而当需要计算和决策的时候,数据有可能已经改动了。
价格表上Talisker威士忌的售价也许已经变了,或是某人可能会修改客户的地址,从而导致运费改变

条件更新和版本戳

“条件更新”(conditional update),客户端执行操作时,将重新读取“商业活动”所依赖的信息,并检测该信息在首次读取之后是否一直没有变动,若一直未变,则将其展示给用户。
通过保证数据库中的记录都有某种形式的“版本戳”(version stamp) 实现“乐观离线锁”(Optimistic Offline Lock) 。
版本戳是一个字段,每当记录中的底层数据改变时,其值也随之改变。
读取数据时可以记下版本戳,这样的话,在写入数据之前,就可以先检查一下数据版本是否已经变了。
使用版本戳避免“更新冲突” ,维护“会话一致性”
“CAS”操作 (“compare-and-set”操作)

  • 既可以由数据库提供
  • 也可以由开发者负责检测的执行

构建版本戳

使用计数器
每当资源更新时,就把它的值加1。根据它的值很容易就能看出哪个版本比较新。
需要服务器来生成该值,并且要有一个主节点来保证不同版本的计数器值不会重复。
使用GUID(全局唯一标识符),也就是一个值很大且保证唯一的随机数。

可以将日期、硬件信息,以及其他一些随机出现的资源组合起来以构建此值。
好处是任何人都可以生成,不用担心重复。
缺点则是数值比较大,而且无法通过直接比较来判断版本的新旧。

根据资源内容生成哈希码(hash)。只要哈希键足够大,那么“内容哈希码”(content hash)就可以像GUID那样全局唯一,而且任何人都可以来生成它。
好处在于,哈希码的内容是确定的:只要资源数据相同,那么任何节点生成的“内容哈希码”都是一样的。
但是,哈希码与GUID一样,都无法通过直接比较来看出版本新旧,而且比较冗长。

使用上一次更新时的时间戳( timestamp)。
与计数器一样,时间戳也相当短小,而且可以直接通过比较其数值来确定版本先后
时间戳不需要由主节点来生成,可以由多台时钟同步的计算机生成。如果某个节点的时钟出错了,那么可能会导致各种数据损毁(data corruption)现象。
若时间戳精确度过低,则可能重复:假如每毫秒都要更新很多次的话,那么将时间戳的精确度设为毫秒是不够的。

可以把几种时间戳生成方案的优点融合起来,同时使用多种手法创建出一个“复合版本戳”(composite stamp)。
在CouchDB创建版本戳时,使用了计数器与“内容哈希码”。
大部分情况下,只要比较版本戳就可以判定两个版本的新旧
万一碰到两个节点同时更新数据的情况,因为两个版本戳的计数器相同,而“内容哈希码”却不同,立刻就能发现冲突。

“主从式复制模型”中的版本戳

在“主从式复制模型”中,只有一个权威数据源(authoritative source for data),使用基本的版本戳生成方案
由主节点负责生成版本戳,而从节点必须使用主节点的版本戳。
以计数器为例,节点每次更新数据时,都将它加1,并把其值放入版本戳中。
假设某主节点有两个副本,分别是“蓝色”节点和“绿色”节点。
如果在蓝色节点所给出的应答数据中,版本戳为4,而绿色节点的版本戳是6,那么绿色节点上的数据就比较新。

“对等式分布模型”中的版本戳

在“对等式分布模型”中,没有统一设置版本戳的节点
如果向两个节点索要同一份数据,那么有可能获得不同的答案
有可能是更新操作已经通知给其中一个节点了,而另外一个节点尚未收到通知
可以选用最新的数据
发生了“更新不一致”现象

在“对等式分布模型”中

  • 确保所有节点都有一份“版本戳记录”(version stamp history)。从而判断出蓝色节点给出的应答数据是不是绿色节点所给数据的“祖先” 。
    • 要么让客户端保存“版本戳记录”,要么由服务器节点来维护此记录,并且把它放在应答数据中,传给客户端。
    • 用“版本戳记录”可以检测出数据“不一致”现象
      • 如果两份应答数据中的版本戳都无法在对方的“版本戳记录”中找到,那么就可以判定发生了“不一致”问题。
  • 使用“时间戳”
    • 很难确保所有节点的时间都一致
    • 无法检测“写入冲突”
数组式版本戳

“数组式版本戳” (vector stamp) 由一系列计数器组成,每个计数器都代表一个节点。
假设有三个节点(分别记为“蓝色(blue)、“绿色”(green)、“黑色”(black)),那么一个可能的“数组式版本戳” 就类似[blue: 43,green :54,black: 12] 。
每当节点执行“内部更新”(internal update)操作时,就将其计数器加1,
假设绿色节点执行了一次更新操作,那么现在这个“数组式版本戳”就成了[blue: 43,green: 55, black: 12]。
只要两个节点通信,它们就同步其“数组式版本戳”。具体的同步方式有很多种。
使用此方案,就能辨别某个“数组式版本戳”是否比另外一个新,因为版本戳中的计数器总是大于或等于旧版本戳。
比如,[blue: 1,green: 2,black: 5]就比[blue: 1, green: 1, black: 5]新

若两个版本戳中都有一个计数器比对方大,那么就发生了“写入冲突”
比如,[blue: 1,green:2,black: 5] 与[blue: 2,green: 1,black: 5]相冲突
数组中可能缺失某些值,我们将其视为0。

  • 比如,[blue: 6, black: 2] 与[blue: 6,green: 0,black: 2]等价。
  • 不需要弃用现有的“数组式版本戳”,就可以向其中轻易新增节点。

“数组式版本戳”是一种能够侦测出“不一致”现象的有用工具,然而它们无法解决此问题。要想解决冲突,就得依赖领域知识。
在“一致性”与延迟之间权衡时。

  • 如果偏向“一致性”,那么系统在出现“网络分区”现象时就无法使用
  • 反之,若要减少延迟,则必须自己检测并处理“不一致”问题。

几个NoSQL数据库

键值数据库

键值数据库(key-value store)是一张简单的哈希表(hash table),主要用在所有数据库访问均通过主键(primary key)来操作的情况下。
可把此表想象成传统的“关系” 该关系有两列:ID与NAME
ID列代表关键字,NAME列存放值。NAME列仅能存放String型的数据。
应用程序可提供ID及VALUE值,并将这一键值对持久化
假如ID已存在,就用新值覆盖当前值,否则就新建一条数据。

键值数据库是最简单的NoSQL数据库。
客户端可以根据键查询值,设置键所对应的值,或从数据库中删除键。
“值”只是数据库存储的一块数据而已,它并不关心也无需知道其中的内容
应用程序负责理解所存数据的含义。
由于键值数据库总是通过主键访问,所以它们一般性能较高,且易于扩展
流行的键值数据库有:Riak、Redis(数据结构服务器)、 Memcached DB及其变种、Berkeley DB、HamsterDB (尤其适合嵌入式开发) 、 Amazon DynamoDB (不开源)和Project Voldemort (Amazon DynamoDB的开源实现)。

在键值数据库中,所存储的聚合不一定是领域对象(domain object),也可以拥有通用数据结构
Redis能够存储list、set、hash 等数据结构,可以支持“获取某个范围内的数值"(range)、“求差集”(diff)、“求并集”( union)、 “求交集”( intersection) 等操作
这些功能使数据库的用途变得比标准键值数据库更多

存储区

“存储区”(bucket)用于区隔关键字的一种手段,可以将其视为存放关键字所用的“平坦命名空间”(flat namespace)

单一存储区

使用单一存储区,把所有数据都放在一个对象里,并将其存入单一的存储区中
将各类对象(也就是聚合)全部存放在一个“存储区”中,其缺点是:“存储区”中可能要存放类型不同的多个聚合,这增加了关键字冲突的几率
还有一种办法是把对象名放在键名后面,例如:288790b8a421_ userProfile, 这样就可用它查出所需的单个对象了

领域存储区

“领域存储区”(domain bucket)来存放特定数据
客户端驱动程序可以对其执行“序列化”(serialization) 与“反序列化”(deserialization) 操作
将跨越多个“存储区”的数据分割成对象,将之存放在“领域存储区”或不同的“存储区”中,这样一来,无需改变关键字的命名方式,即可读出所需对象
存放表达相同含义的不同聚合方案,以应对多种不同应用的需求

  • 效率及数据不一致性问题
一致性

只有针对单个键的操作才具备“一致性”,因为这种操作只可能是“获取”、“设置”或“删除”。
由于数据库无法侦测数值改动, “乐观写入”(optimistic write)功能的实现成本太高。
分布式键值数据库,用“最终一致性模型” 实现“一致性”。
两种解决“更新冲突”的办法:
采纳新写入的数据而拒绝旧数据
将两者(或存在冲突的所有数据)返回给客户端,令其解决冲突

在创建“存储区”时设置与一致性有关的选项。
若想提高数据一致性, 可以规定:执行完写入操作后,只有当存放此数据的全部节点一致将其更新,才认定该操作生效。
显然降低了集群的写入效率
若想提高写入冲突或读取冲突的解决速度,可在创建“存储区”时设置为数据库接纳最新的写入操作,而不再创立“旁系记录”(sibling) 。

事务

不同类型的键值数据库,其“事务”规范也不同,实现“事务”的方式各异。一般说来,无法保证写入操作的“一致性"。
Riak在调用写入数据的API时,它使用W值与复制因子来实现“仲裁”。

  • 假设某个集群的复制因子是5,而W值为3。
  • 在写入数据时,必须有至少3个节点汇报其写入操作已顺利完成,数据库才会认为此操作执行完毕。
  • 由于N等于5而W是3,所以集群在两个节点(N-W=2) 故障时仍可执行写入操作,不过,此时我们无法从那些发生故障的节点中读取某些数据。
查询功能

所有键值数据库都可以按关键字查询。它们的查询功能基本上仅限于此。
如果希望根据“值列”(value column)的某些属性来查询,那么无法用数据库完成此操作
应用程序需要自己读出值,并判断其属性是否符合查询条件。
如果不知道关键字该怎么办?
大部分数据库都不提供全部主键列表,即便提供了,获取关键字列表并查询其值的操作也很烦琐
某些键值数据库支持数值搜寻,以解决此问题
通过API、HTTP(浏览器、Curl等),操作键值数据库

键名的设计

使用键值数据库时,通过某种算法生成键
使用用户信息(例如ID、电子邮件地址等)、时间戳等值,生成键
键值数据库非常适合保存会话(用会话ID作为键)、购物车数据、用户配置等信息

数据结构

键值数据库并不关心键值对里的值。它可以是二进制块、文本、JSON、XML等。
可在HTTP请求中用Content-Type指定数据类型
实质上是由应用判定其内容

可扩展性

很多键值数据库都可用“分片”技术扩展。采用此技术后,键的名字就决定了负责存储该键的节点。
假设按照键名的首字母“分片”。如果键名是f4b19d79587d,那么由于其首字母为f,所以存放它的节点就与存放ad9c7a396542这个键的节点不同。
当集群中的节点数变多时,这种“分片”设定可提高效率。
“分片”也会引发某些问题。假如存放首字母为f的键所用的那个节点坏了,那么其上的数据将无法访问,而且也不能再写入其他键名首字母为f的新数据了。

可以控制“CAP定理” 中的参数: N (存放键值对的副本节点数)、R (顺利完成读取操作所需的最小节点数)和W (顺利完成写入操作所需的最小节点数)。
假设集群有5个节点。将N设为3,意思就是所有数据都至少要复制到3个节点中,将R设为2,意思是GET请求要有两个节点应答,才能成功,将W设为2,意思是PUT请求必须写入两个节点,才算执行完毕。
可以利用这些设置来微调读取及写入操作所能容忍的故障节点数。应该按照应用的需要来改变这些值,以提升数据库的“可读能力”(read availability) 及“可写能力”(write availability)。通常应该根据“一致性”需求来确定W值。
创建“存储区”时可设定上述各参数的默认值。

适用案例

存放会话信息:
通常来说,每一次网络会话都是唯一的,所以分配给它们的sessionid值也各不相同。
如果应用程序原来要把sessionid存在磁盘上或关系型数据库中,那么将其迁移到键值数据库之后,会获益良多

  • 因为全部会话内容都可以用一条PUT请求来存放,而且只需一条GET请求就能取得。
  • 由于会话中的所有信息都放在一个对象中,所以这种“单请求操作”(single-request operation) 很迅速。

存放用户配置信息:
用户配置信息,几乎每位用户都有userId、username或其他独特的属性,而且其配置信息也各自独立,诸如语言、颜色、时区、访问过的产品等。
这些内容可全部放在一个对象里,以便只用一次GET操作即获取某位用户的全部配置信息。
同理,产品信息也可如此存放。

购物车信息:
购物车数据,电子商务网站的用户都与其购物车相绑定。
由于购物车的内容要在不同时间、不同浏览器、不同电脑、不同会话中保持一致,所以可把购物信息放在value属性中,并将其绑定到userid这个键名上。

不适用案例

数据间关系
如果要在不同数据集之间建立关系,或是将不同的关键字集合联系起来,那么即便某些键值数据库提供了“链接遍历”等功能,它们也不是最佳选择。
含有多项操作的事务
如果在保存多个键值对时,其中有一个关键字出错,而又需要复原或回滚其余操作,那么键值数据库就不是最好的解决方案。
查询数据
如果要根据键值对的某部分值来搜寻关键字,那么键值数据库就不是很理想了。我们无法直接检视键值数据库中的值,除非使用某些“检索引擎”(indexing engine)。
操作关键字集合
由于键值数据库一次只能操作一个键,所以它无法同时操作多个关键字。假如需要操作多个关键字,那么最好在客户端处理此问题。

文档数据库

“文档”( document)是文档数据库中的主要概念。
其格式可以是XML、JSON、BSON等。
文档具备自述性(self-describing),呈现分层的树状数据结构(hierarchical tree data structure),可以包含映射表、集合和标量值。
文档彼此相似,但不必完全相同。文档数据库所存放的文档,就相当于键值数据库所存放的“值”。
文档数据库可视为其值可查的键值数据库。

文档数据库中,放在同一“集合”内的各文档的“数据模式”(the schema of the data)可以不同
关系型数据库中,表格中每行数据的模式都要相同。
文档中可以嵌套数组等基本数据类型,也可以将“子文档”(child document) 以“子对象”(subobject) 的形式嵌入主文档。
由于没有“数据模式”约定,文档数据库的文档中无需空属性,若其中不存在某属性,就假定该属性值未设定或与此文档无关。向文档中新增属性时,既无需预先定义,也不用修改已有文档内容。
关系型数据库中,需要定义表中的每一列,而且若某条记录中的某列没有数据,则要将其留空(empty) 或设为null。
流行的文档数据库有: MongoDB、 CouchDB、Terrastore、OrientDB、RavenDB和Lotus Notes。

一致性

通过配置“副本集”(replica set) 实现“复制”,以提供较高的“可用性”
规定写入操作必须等待所写数据复制到全部或是给定数量的从节点之后,才能返回。从而指定数据库的“一致性”强度。
在只有一台服务器时如果指定w为“majority”,那么写入操作立刻就会返回,因为总共只有一个节点。
假设“副本集”中有三个节点,则写入操作必须在至少两个节点上执行完毕,才会视为成功
提升w值可以增强“一致性”,但是会降低写入效率,因为写入操作必须在更多的节点上完成才行。
也可以增加“副本集”的读取效率:设置slaveOk选项之后,就可以于从节点中读取数据了。
参数既可设置到整个"连接”、“数据库”、“集合”之上,也可针对每项操作独立设置。

事务

大多数文档数据库通常没有事务机制:其写入操作要么成功,要么失败。
“单文档级别”(single-document level)的“事务”叫做“原子事务”(atomic transaction)。
可以用不同级别的WriteConcern参数来确保各种安全级别的写入操作
在默认情况下,所有写入操作都将顺利执行。
以WriteConcern.REPLICAS_SAFE为参数写入,即可确保该操作至少要写入两个节点才算成功。
在写日志条目(log entry)时,就可使用最低的安全级别,也就是WriteConcern.NONE。

可用性

文档数据库可以用主从式数据复制技术来增强“可用性”。多个节点都保有同一份数据,即便主节点故障,客户端也依然能获取数据。应用程序代码一般不需检测主节点是否可用。
MongoDB通过“副本集”实现“复制”,以提供较高的“可用性”。副本集中至少有两个节点参与“异步主从式复制”(asynchronous master-slave replication)。
“副本集”通常用于处理“数据冗余”( data redundancy)、“自动故障切换”( automated failover)、 “读取能力扩展”(read scaling)、“无需停机的服务器维护( server maintenance without downtime)和“灾难恢复”(disaster recovery)等事项。
应用程序的写入或读取操作都针对主节点。建立连接后,应用程序只需要同“副本集”中的一个节点相连即可(是不是主节点无所谓),数据库会自动找到其余节点。若主节点故障,则数据库驱动会同“副本集”中新选出的主节点联系。应用程序不用处理通信错误,也无需干预主节点的选拔准则。

副本集在其内部选举“主”(master)节点,或 “主要”(primary)节点。假定所有节点投票权相同,其中某些节点可能会因为距离其他服务器较近,或具有更多运行内存(RAM)等因素而获得更多选票。用户也可以为节点指定一个值在0 ~ 1000之间的优先级( priority)来影响选举过程。
所有请求都由主节点处理,而其数据会复制到从节点。若主节点故障,则“副本集”中剩下的节点就会在其自身范围内选出新的主节点,所有后续请求就交由新的主节点处理,从节点也开始从新的主节点处获取数据。
当原来的主节点从故障中恢复时,它会作为从节点重新加入,并获取全部最新数据,以求与其他节点一致。

查询功能

文档数据库可以查询文档中的数据,而不用像键值数据库那样,必须根据关键字获取整个文档,然后再检视其内容。
CouchDB:可用“物化视图”(materialized view)或“动态视图”(dynamic view)实现复杂的文档查询。
MongoDB支持一种JSON格式的查询语言
由于文档是“聚合对象”(aggregated object),所以用带子对象的字段查询待匹配的文档非常方便。

可扩展性

在不将数据库进行迁移的前提下,向其中新增节点或修改其内容。
增加更多的“读取从节点”(read slave),将读取操作导引至从节点上,这样就可以扩展数据库应对频繁读取的能力了。
假设某个应用程序的读取操作很频繁,可向“副本集”中加入更多从节点,并在执行读取操作时设定slaveOk标志,以提升集群的读取能力。完成读取操作的横向扩展。

如果想扩展写入能力,可以把数据“分片” 。
“分片”与关系型数据库的“分区”类似,
“分区”是根据某列的值,例如状态或年份,将数据分割开。关系型数据库的“分区”通常位于同一节点,所以客户端应用程序只查询“基表”(base table)就好,不需查询某个特定分区,关系型数据库会根据查询内容搜索适当的分区并返回数据。
“分片”操作也根据特定字段来划分数据,然而那些数据要移动到不同的Mongo节点中。为了让各“分片”的负载保持均衡,需要在节点之间动态转移数据。向集群中新增更多节点,并提高可写入的节点数,就能横向扩展其写入能力。
“分片”的关键字很重要。
按照客户名字(first name)来分隔,可确保将数据平衡地散布在各个“分片”上,以获得较好的写入效率。
如果想把 “分片”放在距离用户近的地方,那么可以以用户位置来分片。按客户位置分片时,美国东海岸的全部用户数据都会放在居于东海岸的“分片”中,而所有西海岸的用户数据则将放在位于西海岸的“分片”中。

可以把每个"分片”都做成“副本集”,以提高其读取效率。
如果向已有的“分片集群”(sharded cluster)中再加一个新分片”,就可以把原来分布在3个“分片”中的数据打散到4个“分片”中。
在转移数据与底层设施重构的全过程中,虽说集群为了重新平衡“分片”负载而传输大量数据时性能也许会下降,但是应用程序却无需停止工作。

适用案例

事件记录:
在企业级解决方案中,许多不同的应用程序都需要记录事件。应用程序对事件记录各有需求。
文档数据库可以把所有这些不同类型的事件都存起来,并作为事件存储的“中心数据库”(central data store)使用。
如果事件捕获的数据类型一直在变,那么就更应该用文档数据库了。
可以按照触发事件的应用程序名“分片”,也可以按照order_processed 或customer_logged等事件类型“分片”

内容管理系统及博客平台
由于文档数据库没有“预设模式”(predefined schema),而且通常支持JSON文档,所以它们很适合用在“内容管理系统”(content management system)及网站发布程序上,也可以用来管理用户评论、用户注册、用户配置和面向Web文档(web-facing document)。

网站分析与实时分析
文档数据库可存储实时分析数据。由于可以只更新部分文档内容,所以用它来存储“页面浏览量”(page view)或“独立访客数”( unique visitor)会非常方便,而且无需改变模式即可新增度量标准。

电子商务应用程序
电子商务类应用程序通常需要较为灵活的模式,以存储产品和订单。同时,它们也需要在不做高成本数据库重构及数据迁移的前提下进化其数据模型。

不适用案例

包含多项操作的复杂事务
文档数据库也许不适合执行“跨文档的原子操作”(atomic cross-document operation),虽然像RavenDB等文档数据库其实也支持此类操作。
查询持续变化的聚合结构
灵活的模式意味着数据库对模式不施加任何限制。数据以“应用程序实体”(application entity)的形式存储。
如果要即时查询这些持续改变的实体,那么所用的查询命令也得不停变化(用关系型数据库的术语讲,就是:用JOIN语句将数据表按查询标准连接起来时,待连接的表一直在变)。
由于数据保存在聚合中,所以假如聚合的设计持续变动,那么就需要以“最低级别的粒度”( lowest level of granularity)来保存聚合了,这实际上就等于要统一数据格式了。在这种情况下,文档数据库也许不合适。

列族数据库

列族数据库,可以存储关键字及其映射值,并且可以把值分成多个列族,让每个列族代表一张数据映射表(map of data)。
Cassandra是一款流行的列族数据库,采用对等集群,能快速执行跨集群写入操作并易于对此扩展。
此外还有HBase、Hypertable 和 Amazon DynamoDB等其他产品。

列族数据库将数据存储在列族中,而列族里的行则把许多列数据与本行的“行键”(row key)关联起来。

数据结构

Cassandra的基本存储单元为“列”,列由一个“名值对”(name-value pair)组成,其中的名字也充当关键字。
每个键值对都占据一列,并且都存有一个“时间戳”值。令数据过期、解决写入冲突、处理陈旧数据等操作都会用到时间戳。若某列数据不再使用,则数据库可于稍后的“压缩阶段”(compaction phase)回收其所占空间。
行是列的集合,这些列都附在某个关键字名下,或与之相连。由相似行所构成的集合就是列族。
每个列族都可以与关系型数据库的“行容器”(container of rows)相对照:
两者都用关键字标识行,并且每一行都由多个列组成。
其差别在于,列族数据库的各行不一定要具备完全相同的列,并且可以随意向其中某行加入一列,而不用把它添加到其他行中。

“标准列族”(standard column family)中的列都是“简单列”(simple column) 。
“超列族”(super column family):
如果某列中包含一个由小列组成的映射表,那么它就是“超列”(super column)。可将超列视为“列容器”(container of columns)。
用超列构建的列族叫做“超列族” 。
超列族适合将相关数据存在一起。但是,如果部分列在大部分情况下都用不到,则存在不必要的开销。
“键空间” (keyspace)与关系型数据库中的“数据库”类似,与应用程序有关的全部列族都存放于此。
必须先创建键空间,才能为其增添列族

一致性

Cassandra收到写入请求后,会先将待写数据记录到“提交日志”(commit log)中,然后将其写入内存里一个名为“内存表”(memtable)的结构中。写入操作在写入“提交日志”及“内存表”后,就算成功了。
写入请求成批堆积在内存中,并定期写入一种叫做“SSTable”的结构中。该结构中的缓存一旦写入数据库,就不会再向其继续写入了。
若其数据变动,则需新写一张SSTable。
无用的SSTable可由“压缩”( compaction)操作回收。

若不关心数据是否陈旧,或是需要高效执行读取操作,那么可以将“一致性”设为ONE,以低级别的“一致性”执行读取操作。那么当Cassandra收到读取请求后,会返回第一个副本中的数据
即便其是陈旧数据,也照样返回。
如发现数据陈旧,则启动“读取修复”(read repair)过程
若需要极为高效的写入操作,并且不介意丢失某些写入的数据,那么可以将“一致性”设为ONE,以最低的“一致性”执行写入操作。那么Cassandra只将其写入一个节点的“提交日志”中,然后就向客户端返回响应。
此时,如果某节点在尚未将写入的数据复制到其他节点前出了故障,那么这些数据就会丢失。

若将读取与写入操作的“一致性”都设为QUORUM
那么读取操作将在过半数的节点响应之后,根据时间戳返回最新的列数据给客户端,并通过“读取修复”操作把最新数据复制到那些陈旧的副本中
而“一致性”为QUORUM的写入操作则必须等所写数据传播至过半数的节点后,才能顺利结束其工作并通知客户端。
如果将“一致性”级别设为ALL,那么全部节点就必须响应读取或写入操作
这将使集群失去容错能力:一旦某个节点故障,全部读取操作或写入操作都将阻塞并失败。
系统设计师应根据应用程序需求调整“一致性”级别,同一应用程序内部也会有不同的“一致性”需求,所以也可以针对每次操作来设定其“一致性”。
例如,显示产品评论所需的“一致性”,就与读取客户所下最新订单状态不同

在创建“键空间”时,可以配置存储数据用的副本数,它决定了数据的“复制因子”。
若复制因子为3,则数据将复制至3个节点上。使用Cassandra写入及读取数据时,若将“一致性”设为2,则R+W的值就会大于复制因子(2+2>3), 这使得读取操作与写入操作的“一致性”都比较好。
可以在“键空间”上执行“节点修复”(node repair)命令,这会迫使Cassandra将其负责的每一个关键字与其余副本相比对。
由于此操作开销较大,所以有时可以只修复一个或一组列族。
若某节点故障,则其存储数据会移交给其他节点。而当它重新上线时,数据库会把变更后的数据交还此节点。这种技术叫做“提示移交”(hinted handoff),它可以帮助故障节点更快地恢复。

事务

Cassandra没有传统意义上的“事务”(即封装多个写入操作并决定是否提交其数据变更的单元)。
Cassandra 的写入操作在“行”级别是“原子的”
根据某个给定的行键向行中插入或更新多个列,将算作一个写入操作,它要么成功,要么失败。
写入操作首先会写在“提交日志”及“内存表”中,只有它向这两者写入数据后,才算顺利执行完
假如某节点故障,稍后可根据“提交日志”将数据变更恢复至该节点中,这与Oracle数据库中的“重做日志”(redo log) 类似。
可用ZooKeeper等外部的“事务”程序库同步读写操作。还有Cages等程序库可把ZooKeeper形式的“事务”封装起来。

可用性

因为集群里没有主节点,其中每个节点地位等同。
在“一致性”与“可用性”之间做出明智的权衡。减少操作请求的“一致性”级别,即可提升集群“可用性”。
(R+W) >N。
W是成功执行写入操作所需的最小节点数
R是顺利执行读取操作所需获取的最小应答节点数
N是参与数据复制的节点数。
对于某定值N,可改变R与W的值,以调整“可用性”。

假设在10节点的Cassandra集群中,有一个复制因子为3的“键空间”(N=3)。
如果R=2且W=2,那么(2+2) >3。在此情况下,若有一个节点故障,则不影响“可用性”,因为数据还可以从其他两个节点中获得。
若W=2而R=1,则集群在两个节点故障时将无法写入,但仍可读取。
若R=2而W=1,则集群在两个节点故障时仍可写入,但无法读取。
“键空间”与“读/写操作”应该按照需求来设置:要么提高写入操作的“可用性”,要么提高读取操作的“可用性”。

查询功能

由于Cassandra没有功能丰富的查询语言,所以在设计其数据模型时,应该优化列与列族,以提升数据读取速度。
在列族中插入数据后,每行中的数据都会按列名排序。
假如某一列的获取次数比其他列更频繁,那么为了性能起见,应该将其值用作行键。
基本操作:
指定“键空间”作为查询范围
通过CREATE定义列族
通过SET向列族插入数据或更新数据
通过GET获取整个列族或列族所需的一列
通过DEL删去一列或整个列族

高级查询与索引编订:
Cassandra的列族可以用关键字之外的其他列当索引。
然后直接通过索引进行查询
索引以“位映射图”(bit-mapped) 的形式实现,在列中频繁出现重复数值的情况下,性能较好。
Cassandra查询语言(CQL) ,Cassandra支持一种类似SQL命令的查询语言,叫做“Cassandra查询语言”(Cassandra Query Language,简称CQL)。
CQL命令可以创建列族,插入数据,读取读出全部列或者只读取需要的列,为列创立索引,并根据索引查询数据
CQL中还有很多查询数据的功能,不过它并未包含SQL的全部功能。CQL不支持“连接”(JOIN)及“子查询”(subquery),而且其where子句通常也比较简单。

可扩展性

在已有的Cassandra集群中扩展,也就意味着增加更多节点。
由于不存在主节点,所以向集群中新增节点后,即可改善其服务能力,令其可以处理更多的写入及读取操作。
这种横向扩展可以尽力提高其正常运行时间,因为集群在新增节点时,仍能处理客户端请求。

适用案例

事件记录:
由于列族数据库可存放任意数据结构,所以它很适合用来保存应用程序状态或运行中遇到的错误等事件信息。
在企业级环境下,所有应用程序都可以把事件写入Cassandra数据库。它们可以用appname: timestamp (应用程序名:时间戳)作为行键,并使用自己需要的列。
由于Cassandra的写入能力可扩展,所以在事件记录系统中使用它效果会很好。

内容管理系统与博客平台:
使用列族,可以把博文的“标签”(tag)、“类别”(category)、“链接" (link)和“trackback”日等属性放在不同的列中。
评论信息既可以与上述内容放在同一行,也可以移到另一个“键空间”。
同理,博客用户与实际博文亦可存于不同列族中。

计数器
在网络应用程序中,通常要统计某页面的访问人数并对其分类,以算出分析数据。此时可使用CounterColumnType来创建列族。
创建好列族后,可以使用任意列记录网络应用程序中每个用户访问每一页面的次数。
也可以用CQL增加计数器的值
限期
可能需要向用户提供试用版,或是在网站上将某个广告条显示一定时间。这些功能可以通过“带过期时限的列”(expiringcolumn)来完成。
这种列过了给定时限后,就会由Cassandra自动删除。这个时限叫做TTL (Time To Live,生存时间),以秒为单位。
经过TTL指定的时长后,这种列就被删掉了。程序若检测到此列不存在,则可收回用户访问权限或移除广告条。

不适用案例

需要以“ACID事务”执行写入及读取操作的系统。
如果想让数据库根据查询结果来聚合数据(例如SUM(求和)或AVG (求平均值)),那么得把每一行数据都读到客户端,并在此执行操作。
在开发早期原型或刚开始试探某个技术方案时,不太适合用Cassandra。开发初期无法确定查询模式的变化情况,而查询模式一旦改变,列族的设计也要随之修改。这将阻碍产品创新团队的工作并降低开发者的生产能力。
在关系型数据库中,数据模式的修改成本很高,而这却降低了查询模式的修改成本
Cassandra 则与之相反,改变其查询模式要比改变其数据模式代价更高。

图数据库

图数据库可存放实体及实体间关系。
实体也叫“节点”(node),它们具有属性(property)。可将节点视为应用程序中某对象的实例。
关系又叫“边”(edge),它们也有属性。边具备方向性( directional significance),而节点则按关系组织起来,以便在其中查找所需模式。
用图将数据一次性组织好,稍后便可根据“关系”以不同方式解读它。
图数据库有很多种,如Neo4J、Infinite Graph、OrientDB和FlockDB等
FlockDB 是个特例,它仅支持单深度的(single-depth)关系及邻接表(adjacency list),所以无法遍历深度超过1的关系

关系型数据库 vs 图数据库

在关系型数据库中存储的图状结构(graph-like structure),通常是单一关系类型(常见的例子:“我的经理是谁”)。
向已有数据中增加另一条关系,一般要修改许多模式并转移大批数据
关系型数据库必须根据需要遍历的内容(Traversal) 提前建好图模型,若待遍历的内容改变,则数据也要随之变动。

图数据库中,节点间可有多种不同的关系类型,由于节点关系的数量及类型不限,所以这些关系可存放在同一图数据库中。这样既能表现领域实体(domain entity) 之间的关系,也可以表示辅助关系( secondary relationship)
在图数据库中,无需改变节点或边,即可应对新的遍历需求。
图数据库遍历“连接”及“关系”非常快。节点间的关系不在查询时计算,而是在创建时已经持久化了。遍历持久化之后的关系,要比每次查询时都计算关系更快。

数据结构

在Neo4J中创建图很简单,只需建立两个节点及其关系即可。现在创建两个名为Martin和Pramod的节点
我们将这两个节点的name (名称)属性分别赋值为Martin及Pramod。
只要节点数大于1,即可创建关系
必须从两个方向来创建节点间关系,因为关系的方向很重要。
例如,product (产品)节点可以受user (用户)欢迎(be liked by),但product不可能“喜欢”(like) user。
方向性(directionality) 有助于设计出丰富的领域模型。可以根据传入关系(INCOMING relationship)与传出关系(OUTGOING relationship) 双向遍历节点。

图数据库中的大多数值都源自关系。
关系不只含有类型、起始节点和终止节点,而且还有自己的属性。使用这些属性,可令关系更智能。
例如,可以指定两人之间何时成为朋友,两节点间距多远,两节点共享何种内容等。
这些关系上的属性可用于查询图。
由于图数据库基本上得力于关系及其属性,因此在建模所要面对的领域关系时,需做大量思考与设计。
新增关系类型比较容易,而要改变已有节点及其关系,则相当于数据迁移了,因为现存数据的每个节点及关系都要变动。

一致性

由于图数据库操作互相连接的节点,所以大部分图数据库通常不支持把节点分布在不同服务器上。
然而,Infinite Graph等某些解决方案,可以把节点分布在集群中的服务器上。
在单服务器环境下,数据总是一致的,尤其是Neo4J这种完全兼容ACID事务的(fully ACID-compliant)数据库。
如果在集群上运行Neo4J,那么写入主节点的数据会逐渐同步至从节点,而读取操作则总是可在从节点执行。也可以向从节点写入数据,所写数据将立刻同步至主节点,但是其他从节点并不会立刻同步,它们必须等待由主节点传播过来的数据。
图数据库通过事务来保证“一致性”。不允许出现“悬挂关系”(dangling relationship) :所有关系必须具备起始节点与终止节点,而且在删除节点前,必须先移除其上的关系

事务

Neo4J是兼容ACID事务的数据库。修改节点或向现有节点新增关系前,必须先启动事务。
若未将操作包装在事务中,则可能会抛出NotInTransactionException。
先在数据库上发起事务,然后创建节点并设置其属性。接下来,将事务标注为success ( 成功),最后调用finish方法完成此事务。事务必须标注为success,否则Neo4J就假定它失败了,并会在执行finish时回滚。仅设定success而不执行finish,也会导致数据提交不到数据库
读取操作可不通过事务执行。

可用性

Neo4J支持“副本从节点”(replicated slave),并借此获得较高的可用性。
这些从节点可处理写入请求:向其写入后,它会先将所写数据同步至当前主节点。写入操作会先提交至主节点,然后再提交至从节点。其他从节点将逐渐获得更新数据。
Infinite Graph与FlockDB等图数据库则支持分布式节点存储。
Neo4J使用Apache ZooKeeper来记录每个从节点及当前主节点中最新的事务ID。服务器启动后,将与ZooKeeper通信,以找出主服务器。若该服务器第一个加入集群,则它就成了主节点。当主节点故障时,集群将在可用节点中新选主节点,因此极具可用性。

查询功能

图数据库可以使用查询语言。
Gremlin 是一门可以遍历图的领域特定语言(domain-specific language), 它可以遍历所有实现了Blueprints属性图(property graph)的图数据库。
Neo4J 也可用Cypher查询语言来查询图。
除了查询语言之外,Neo4J 还可以按节点属性查询图、遍历图,或通过“语言绑定”(language binding)浏览节点关系。
可以用“索引服务”(indexing service)来编订节点属性索引。同理,也可以索引关系或边的属性,以便根据属性值来查询。
在向数据库中添加节点时,就可以为其编订索引来搜寻节点
Neo4J 以Lucene为其索引服务
也可以稍后遍历全部节点时再对其索引
将节点加入索引这一操作,必须在事务中执行

开始遍历图之前,应先查询索引以找出起始节点。
节点编入索引后,即可用索引属性搜寻之。
若想查找名叫Barbara的节点,可在索引中以Barbara为name属性值来搜寻。
找到了节点,就可以得知其全部关系了。
查询关系时也可以按其方向过滤搜索结果,可以分别获取INCOMING (传入)与OUTGOING (传出)关系。
假设在图11.1中,要找出所有“喜欢”(like) NoSQL Distilled 的人,那么可以先找到NoSQL Distilled 节点,然后搜寻INCOMING关系。
由于我们只想寻找与NoSQL Distilled有LIKE (喜欢)关系的节点,所以在过滤搜索结果时,还要加上关系类型这一项标准。
寻找节点及其直接关系较为容易,也能用关系型数据库来做。

**图数据库真正强大之处在于,它可以从指定的起始节点开始,以任意深度遍历图。**在待查节点同起始节点之间的关系深度大于1时,图数据库尤为有用。如果图的深度较大,那就更应该使用Traverser来遍历其关系了,遍历时可指明关系类型是INCOMING(传入)、OUTGOING (传出),还是BOTH (两者皆可)。
还可以用BREADTH FIRST (广度优先)或DEPTH_ FIRST (深度优先)为Order值,告诉遍历器( traverser)先向两旁搜索还是先自顶向下搜索。
遍历器必须有起始节点。
可以不限深度地查找所有与Barbara有FRIEND (朋友)关系的节点。可以找出所有与Barbara的关系类型为FRIEND的节点。待查节点深度不限,即使是“朋友的朋友”也行,不管中间经过多少层间接关系。这个遍历器会探寻整个树状结构(tree structure)。

图数据库的一个优点就是能找到两节点间的路径,也就是判断它们之间是否有多条路径,如果有,就找出全部路径或最短路径。也可以将其他图算法运用到待搜寻的图中。例如可用Dijkstra算法求出节点间最短路径或“开销最小的路径”(cheapest path)。

Neo4J还提供了Cypher 查询语言来查询图。
Cypher需要以一个节点“启动”(START)查询。起始节点可通过节点ID或节点ID列表指明,也可用索引查出。
Cypher使用MATCH关键字匹配关系中的模式,以WHERE关键字过滤节点或关系的属性。
RETURN关键字则指定了查询所返回的数据是节点,是关系,还是节点或关系中的字段。
Cypher也提供了排序(ORDER)、聚合(AGGREGATE)、略过(SKIP)和限定(LIMIT)数据所用的方法。
Cypher使用符号表达查询图数据库的功能

可扩展性

NoSQL数据库最常用的扩展技术就是“分片”,也就是把数据分割并存放在不同服务器上。对图数据库分片比较难,因为它们不是面向聚合的( aggregate-oriented),而是面向关系的( relationship-oriented)。
由于任何节点都可能关联其他节点,所以把相关节点放在同一台服务器中,遍历图时会更方便些。若图中的节点放在不同电脑上,则遍历性能不佳。
尽管图数据库有此局限,仍可用一些常用技术扩展它们。

由于时下电脑内存都较大,所以可给服务器配备足量内存,使之可完全容纳“工作集”(workingset)中的全部节点与关系。只有当存放工作数据集所需的内存量比较合理时,这项技术才能派上用场。
增加仅能读取数据的从节点,即可改善数据库读取能力,所有写入操作仍由主节点负责。使用此模式,只需向主节点写入一次数据,就能从许多服务器中读取了。这是一种成熟的MySQL集群技术,当数据集大到无法容纳于单机内存,而又足以在多台服务器之间复制时,这项技术尤为有用。我们可以配置从节点,让其只能用于读取数据而不会成为主节点,这样就有助于扩展数据库的“可用性”及读取能力了。
若数据集太大,导致多节点复制不太现实,那么可用“领域特定知识”(domain-specific knowledge)在应用程序端对其分片。
例如,可将与北美有关的节点放在一台服务器上,而把和亚洲有关的节点放在另一台上。使用“应用程序级分片”(application-level sharding)时,必须明白,节点存放在地理位置不同的数据库中。

适用案例

互联数据:
部署并使用图数据库来处理社交网络非常高效。社交图里并不是只能有“朋友”这种关系,例如也可以用它们表示雇员、雇员的学识,以及这些雇员与其他雇员在不同项目中的工作位置。
任何富含链接关系的领域都很适合用图数据库表示。
假如同一个数据库含有不同领域(像社交领域、空间领域、商务领域等)的领域实体,而这些实体之间又有关系,那么图数据库提供的跨领域遍历功能,可以让这些关系变得更有价值。

安排运输路线、分派货物和基于位置的服务:
投递过程中的每个地点或地址都是一个节点,可以把送货员投递货物时所经全部节点建模为一张节点图。
节点间关系可带有距离属性,以便高效投递货物。
距离与位置属性也可用在兴趣点图(graph of places of interest)中,这样应用程序就可向用户推荐其附近的好餐馆及娱乐场所了。
还可将书店、餐馆等销售点(point of sales)做成节点,当用户靠近时通知他们,以提供基于位置的服务。

推荐引擎:
在系统中创建节点与关系时,可以用它们为客户推荐信息,例如“您的朋友也买了这件产品”或“给这些货品开发票时,通常也要为那些货品一并开票”。
还可以用它们向旅行者提议:来巴塞罗那旅游的人一般都会去看看安东尼●高迪所设计的建筑。
用图数据库推荐信息时,有个副作用值得注意:随着数据量变多,推荐信息所用的节点及关系数也激增。同一份数据可以挖掘出不同信息
例如,既可以从中看出客户总是将其与哪些产品一并购买,也可以查出与此产品一并开发票的其余产品。
若两者不匹配,则可发出警示。图数据库与其他“推荐引擎”(recommendation engine)一样,也可以根据关系间的模式侦测交易欺诈(fraud intransaction)。

不适用案例

图数据库在更新全部或某子集内的实体时也许不适用。
比如,在某个“数据分析解决方案”(analytics solution)中,只要一个属性变了,全部实体就都得更新。
此时图数据库的效果就不理想了,因为没有哪个简单的操作能一次性改变所有 节点中的某个属性。
即便数据模型适合问题领域,某些图数据库可能也无法处理那么大的数据量,尤其在执行“全局图操作”(global graph operation,涉及整张图的操作)时更是如此。

Part12——总结

NoSQL章节真是又臭又长,其实很多内容我并未去细看。经过树立,不难看出考试的几个重点:
应用题:E-R图、关系代数与SQL语句、范式
问答题:nosql * 2,零散的一些知识点概念

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值