Skip to content

数据库系统导学

数据库系统是什么?

基于文件的应用

  在程序运行的过程中,总会产生一些需要保存的数据,就像文档需要存储docx文件,P图需要存储psd文件,这些数据被存在这些特殊的文件中,需要word和photoshop等专用的软件来处理。

  然而我们容易观察到,这些软件的占用空间并不小。因为开发这些文件系统需要考虑非常多的情况。比如要考虑自动保存,文件加密和安全,多用户协作等等。如果我们操作数据都要下载一个极大占用空间的软件,那么我们的存储空间是远远跟不上我们的需求的。

  对于企业而言,重复地造轮子也会消耗大量的成本和大量的时间,造成资源的浪费和真正项目的停滞不前。


  为了解决这个问题,许多公司选择共用一个数据系统,数据库系统也就随之诞生。

基于数据库的应用

  由于有数据库系统来管理数据,整个应用文件就不需要包含过多的数据存储部分,会变得更加轻量。同时,由于数据库系统的设计,数据的存放也会变得安全,更有鲁棒性。同时,数据库系统也在多用户协作的方面提供了并发性控制,防止数据修改的冲突。

什么是鲁棒性

鲁棒性(Robustness)是指系统在遇到异常情况时仍能保持其正确性、稳定性和可靠性。

数据库系统有什么用?

  数据库系统从企业、制造业、银行、教育、交通、电子商务等各种领域都有非常广泛地应用。

  举个例子,就在校园中,我们的校园卡中存储的就是我们消费的信息,我们的选课依赖教务系统的数据库,线上的智云课堂存储着课程视频的各种信息,等等等等。所以说数据库的应用覆盖着我们生活的方方面面,是信息化的基石。

  起初,数据库的规模很小,就像我们手机空间存储不够会清理存储一样,当时的数据库系统会清理旧的数据以接受新的数据。

  然而随着数据的需求不断地增大,特别是互联网应用的爆发式增长产生大量的数据,我们很快地步入了大数据的时代。因此,数据库构建的要求也越来越高。

  在数字经济中,数据要素也就成为了不可或缺的要素之一,构建一个高效、安全、可靠的数据库系统,也就奠定着数字经济的基础。

大数据推进人工智能

  在人工智能领域中有一个术语叫做“训练”,也就是要通过数据来让机器学习出更真实的行为。而就像我们在高中时代需要做大量的题取得好分数,机器同样需要大量的数据才能更好地模拟和预测。

一个典型的数据库——银行

  • 内容
    • 用户表
    • 账户表
    • 用户-账户关系表
  • 基本操作
  • 增加客户
  • 创建账户
  • 存款/取款
  • 借贷/还款

为什么要开发数据库系统?

1. 数据冗余和不一致

  就像我们用Photoshop修图,存下psd文件,在另一台电脑上使用的时候,我们需要把这个psd文件给复制过去后方能使用。对于psd这样轻量的文件尚可这样操作,然而在大数据时代,数据复制的成本是非常大的。数据库系统能很大程度上规避因为数据拷贝而导致的数据冗余,同时还避免了拷贝错误带来的数据不一致。

2. 数据孤岛

  设想A用2004版本制作了一份ppt,而B用2024版本的powerpoint打开,因为版本的不兼容,会导致图像错位,字体丢失等等问题。这是因为2004版和2024版数据存储的结构是不同的。使用数据库系统可以达到数据存储结构的统一,规避数据孤岛的出现。

3. 数据访问困难

  在软件编写层面,如果要访问查找一项数据,如果只简单地依赖于文件系统则需要自行编写查询语句(for循环简易但是效率低,效率高的b树实现繁琐),费时费力费空间。而数据库系统则能提供一种统一的数据访问接口,直接用一条语句就可以完成数据的访问和查找。

4. 完整性问题

  在访问和查找的过程中,我们经常要设置一些限制条件。在软件编写层面,实现可变的限制条件非常繁琐,而数据库系统已经提供好了改变限制条件的接口,极大地方便访问。

5. 原子性问题

   原子性的“原子”来自于古希腊德谟克利特的原子论。理论认为,原子是不可分割的最小粒子。反映到数据库中,数据库系统中的原子性指的也就是它的每一个操作都是不可分割的。

  举个例子,假设一个银行系统的A账户要向B账户转账,那么整体就会有两个子操作,A账户扣款和B账户存款。这两个子操作必须同时成功,或者同时失败,它们是不可分割的,这就是数据库系统的原子性。

6. 并发访问异常

  并发访问可以视为一种同时的访问。举个例子,假设一个银行系统,A客户和B客户同时向C账户转账,那么就可能出现并发转账问题。

  问题是怎么出现的呢?转账到C账户的过程可以分为两个子操作,读取C账户的余额,C账户增加余额。如果A和B客户同时读取C账户的余额,那么就会读取到同一个余额(假如是100元),假设A和B都转了50元,那么最终C账户的余额会是150元,而不是200元。这就是并发访问异常。

  我觉得这也可以视为操作的一种不可分割性,在指令处理上一个操作必须是完整的,不宜插入其它的语句。通过数据库系统可以很好地解决这个问题。

7. 安全性问题

  有的时候,我们不希望某些数据被访问,或者希望某些数据只能被某些人访问,并且我们还要防止有权限操作的人员没有非法行为。这就涉及到数据库系统的安全性问题。

  完整的安全性包含三个层面的设计,认证、权限和审计。

  认证指的是让用户登录验证自己的身份,权限指的是验证用户是否有权限访问某些数据,审计指的是记录用户对哪些数据进行了哪些操作,用来检查恶意的非法操作。

  实现这三个层面需要涉及非常多的如密码学各领域的知识,如果只使用简单的文件管理系统,完整的实现依然是费时费力费空间。数据库系统可以提供相应的接口,实现对数据的访问控制,轻松解决安全性的问题。

总结一下上面的七个例子,我们就可以得到如下的数据库系统的优点——

  1. 数据持久性
  2. 数据访问便利性
  3. 数据完整性
  4. 多用户并发控制
  5. 故障恢复
  6. 安全控制

  其中 1. 来自数据库系统的定义,2. 3. 4. 6. 是上述问题的解决方式,5. 是实现原子性的一种实际操作。

数据模型

  既然我们需要构建一个高效的数据库系统,那么一个清晰可观的数据模型是必不可少的。在数据库的发展中,诞生了非常多类型的数据库。

关系型数据模型

  关系型数据库是数据库系统中最常见的一种。简单地来说关系型数据库由一一对应的关系构成,这种一一对应的关系就可以直截了当地用表格来画出来。所以,可以联想到关系型数据库的一些基本的操作——横向可以做选择,纵向可以做投影,两张表可以建立关系,进行集合操作,等等。

面向对象数据模型

  面向对象型数据库是吸收了面向对象的思想,从而构建的一种数据库类型。举个例子,在游戏Minecraft中,每一个生物都有类型、血量等属性,同时也可以做出行走、跳跃等行为,把这些属性、行为做一个总结模板,就可以得到一个类,也就是对象,面向对象的数据模型就可以视为一个一个对象构建起来的。

对象-关系数据模型

  随着数据库的发展,许多主流的数据库吸收了面向对象的思想,兼容了关系型模型的直观以及面向对象的灵活,发展出了对象-关系数据模型。这种模型兼容了关系型数据库和面向对象数据模型的特性,可以说是两者精华的结合。

半结构化数据模型

  首先,要明确什么是结构化。结构化模型提供了清晰的结构和索引,这样机器就能够快速地检查数据。而非结构化模型就是机器没有办法简单地按照索引去识别的存储。举个例子,表格给予的索引可以让机器非常精准地识别数据,但是比如一长段文本,或者一张图像,其中包含的信息量是不能简单地靠索引来识别的,需要进一步做分析。

  半结构化的数据,就是既包含结构化信息,又包含非结构化信息的数据。举个例子,就像一篇作文有起承转合的段落,这是结构化的思想,而其中的内容和细节需要做进一步的区分,这就是非结构化的思想。

其它模型

  在数据库的发展进程中中产生了非常多的模型,比较经典的有网状模型层次模型等等。

  在数据库的设计层面,一个经典的模型就是实体-关系模型。还是举minecraft的例子。MC的生物其实还有另外一个名称——实体,每一个实体都有自己的属性,比如血量、位置、行为等等。而关系将实体与实体之间建立联系,比如两个村民之间需要投喂食物,或者僵尸需要攻击村民,等等。通过这个模型,我们就可以形象地把我们需要的数据表现出来。

关系型数据模型——进一步地..

  因为它的经典性和广泛的使用,关系型数据模型将会成为进一步学习的重点模型。

  我们知道,因为关系型的一一对应关系,关系型数据库可以以表格的形式来呈现。对于这个表格,一些我们常见的概念有了新的叫法——

  • 元组 - 它用来表示一个事物的所有数据值,反映在表上就是列表的每一行。
  • 属性 - 它是事物的一种数据值,反映在表上就是列表的每一列。
  • 主键 - 为了区分事物,我们需要一种独一无二的属性,这种属性可以是身份证号、序号,等等,它们被称作主键。
  • 外部键 - 有的时候,我们需要通过下一层的数据查询上一层的信息。举个例子,我想要查询学生天童爱丽丝所属学院(↓↓,↑)的负责人,就要先查询她是哪个学院的(千年),链接到和那个学院相关的表格,再进行查询。外部键就是用来建立这种链接关系的。需要注意的是,首先外部键链接到的属性在那张表上一定是唯一的,也就是这个属性是一个主键。其次,外部键的值是可以对应的,这样才能建立联系。

数据的不同呈现方式

  在数据库的设计中,面对不同的人(或者物),我们需要设计出不同的数据呈现方式。比如,对于具体使用的客户和管理员,我们需要以一种视图模式来呈现;面对数据库的设计者,我们需要呈现更多的逻辑联系信息,也就是以一种逻辑模式来呈现;面向数据库的底层维护者,或者说对机器本身,我们就需要以一种物理模式来呈现。所以,可以看到,整个数据库面向不同的人,从底层到应用层分成了三种结构——物理模式、逻辑模式和试图模式。

物理模式

  物理模式反映的是数据库的底层结构,它存储的是数据最原始的形态,比如数据存在哪台机器上,哪个磁盘上,哪个文件路径上,以什么数据结构存储等等,从而提供相应的索引方便机器来检索。

逻辑模式

  逻辑模式反映的是数据库的逻辑结构,它存储的是经过数据模型整理后,数据的逻辑结构,比如数据库有哪些属性,主键是哪一个,等等。它还包含了数据模型中定义的逻辑关系,比如两张表的联系关系。这相当于提供了一个设计图,告诉设计者,这个数据库长什么样子。

视图模式

  视图模式是给用户使用的一种高度图形化的、干净方便快捷、同时有所限制的一种模式。它隐藏了数据类型、逻辑联系等细节,给用户提供最直观的数据。值得注意的是,视图模式是不止一种的,因为不同的用户对数据的需求是不同的,所以我们需要提供不同的视图模式。比如,对于普通用户,我们可能需要提供一种只读的视图模式,并且根据权限控制可以只开放部分数据的访问;对于管理员,我们可能需要提供一种可以修改的视图模式,并且相对于普通用户有访问更多数据的权限。

模式和实例

  就像C语言中,我们用类型来定义一个变量,随着时间的变化这个变量的值会发生变化,但是它的类型是不会变的。同样的,模式和实例的关系也是类似的。实例是千变万化的数据库内容,模式是万变不离其宗的框架结构。在数据库的使用过程中,它的模式是固定的,但是实例是会变化的。

构建模式的好处

  首先,构建因人而异的模式在很大程度上隐藏了数据库的复杂性,它不会把数据库的全貌呈现给每一个人,而是按需来呈现。其次,构建模式可以提高数据库的灵活性和适应性,当我们需要修改某一个模式的内容的时候,我们没有必要牵一发而动全身去重新设计,只需要调整模式之间的对应关系即可。

  因此,我们就知道了数据库的一个重要性质——数据的独立性。在修改物理模式的时候,我们可以不改变逻辑模式,这是物理数据独立性;而修改逻辑模式的时候,也可以不改变物理模式,这是逻辑数据独立性

数据库的语言

  很多时候我们需要通过编程来实现自动化,而数据库就提供了多种语言,来方便地对数据库的操作进行编程,根据语言的用途,分为数据定义语言数据操作语言

数据定义语言

  就像我们使用struct来定义C语言中的一个结构,在数据库中,我们同样使用数据定义语言来定义一个结构。在编译之后,C语言中的struct会生成一个符号表,同样地,在数据库中,数据定义语言也会生成一个数据字典,表格的模板被存放在这个数据字典中。

数据定义语言的例子

CREATE TABLE instructor (
    ID char(5),
    name varchar(20),
    dept_name varchar(20),
    salary numeric(8,2),
    PRIMARY KEY (ID),
    FOREIGN KEY (dept_name) REFERENCES department(name)
);
  上面代码的意思是,我们创建了一个名为instructor的表,它包含四个字段:ID、name、dept_name和salary。其中,ID是主键,dept_name是外键,它引用了department表中的name字段。

  然而,需要注意的是,数据定义语言的结构和C语言中的struct有所区别,C语言的struct在经过链接后会直接把符号表丢弃,用指针来代替,但是数据库系统不会丢弃这些表格模板,而会实实在在地把它们作为一种数据存储下来,这种数据就叫元数据

模式

  还记得先前对模式的定义吗?模式就是一种归纳出来的总体结构,我们发现元数据的定义是吻合这种定义的。所以它蕴含着一种“数据库模式”。

  在元数据中,除了属性类型之外,我们还可以看到为了维护完整性的语义而设置的相关约束,包括上面的主键和外键。实际上,主键代表着个体的完整性,而外键则暗示着参照的完整性(外键的指向属性必须是主键)。此外,一些权限限制也会存储在元数据当中。

数据操作语言

  数据操作语言,也就是查询语言,是用于查找和操作数据库中的数据的语言。其中,使用的最为广泛的就是结构化查询语言(SQL)。

SQL和传统编程语言的区别

  我们之前学过的c,python,java等编程语言,实际上是按照一个流程来进行编程的,这类编程语言被成为过程式的。SQL不同,它的语句比较简单精炼,就像说一句话,而不像写一段程序。这类编程语言被称作陈述式(非过程式)语言。

SQL的例子

    select name
    from instructor
    where instructor.ID = '10129';
  上面代码的意思是,从instructor表中查找name字段,条件是instructor.ID等于'10129'。
    select instructor.ID, department.building
    from instructor, department
    where instructor.dept_name = department.dept_name and department.dept_name = 'Physics';
  上面代码的意思是,从instructor和department表中查找instructor.ID和department.building字段,条件是instructor.dept_name等于department.dept_name并且department.dept_name等于'Physics'。

  但是,我们发现这种语句的用途非常有限,它不能够像流水线一样为我们实现自动化,处理输入输出。所以在实际应用中,我们会把数据操作语言和传统的过程式语言结合起来,使用SQL来从数据库中读取数据,然后使用C/C++,Python,Java来处理数据。后者被称作宿主语言

  那么如何将数据操作语言嵌入到宿主语言当中呢?实际上,数据库提供了一种接口,比如ODBC,JDBC,ADO.NET等,这些接口可以允许外部的数据操作语言和数据库之间进行通信。此外,宿主语言会提供外部插件,来适配数据操作语言在宿主语言中的使用。

数据库设计

  搞清楚了整个数据库的结构和操作方式之后,我们从用户和开发者的视角了解了数据库,然而这是不够的,我们还需要站在一个设计者的角度去学习数据库的设计方式。

  在刚刚我们提到了一个特殊的模型,就是实体-联系模型。我们刚刚介绍了实体,现在我们来谈谈联系。联系分一对一、多对一、一对多、多对多的关系,这样的关系在之后的模型构建中非常重要。

  分析完模型后,我们可以按部就班地设计数据库。然而,在数据库设计完成的时候,我们不知道这个设计是否是成功的,我们需要对它进行一些测试和评估,因此,规范化理论将会被引入,来帮助我们进行分析,是否有信息冗余和不一致。


  以上是上半个学期数据库系统将会涉及到的知识,更多地是在传授概念,介绍数据库系统的相关内容;而在下半个学期,将会涉及算法的知识,介绍数据库从底层的方面如何实现。

数据库引擎

  数据库引擎是驱动整个数据库运作的框架体系,它在物理底层实现数据的快速读取,在逻辑层面实现数据语言的高效识别和执行,在用户层面提供高性能、高安全性、原子性的事务管理。这三个方面分别对应了数据库引擎的存储管理模块、询问处理模块和事务管理模块。

存储管理模块

  作为一个编程模块,存储管理模块向下完成底层数据的读取,从而为上层的查询处理模块提供数据读取的接口。数据可以是源数据,可以是存储元数据的数据字典,也可以是提供查询的数字索引、以及预先计算的统计数据,等等。

  然而,数据的读取绝非C语言的一句Scanf如此简单,它是直接与操作系统的文件管理器进行交互的。因此,因为底层硬件的限制,还需要考虑更多处理:

  • 文件管理

  计算机底层内容的读取不是以字节为单位的,而是以块为单位的,因此很多时候我们需要将一个文件分成多个块,然后将多个块进行读取。这个时候就需要考虑块是否有截断,记录是否完整,等等因素,它们就需要交给文件管理来处理。

  • 缓存管理

  在计算机的存储中,有外部存储和内部存储,外部存储的读取速度远远慢于内部存储。为了减少内部存储的浪费,我们将部分外部存储读入的数据暂时地存在内存的一个位置,这个就是缓冲区。对缓冲区的设计、特别是在缓冲区写满后缓冲区的调度策略设计就是缓存管理需要考虑的问题。

  • 授权和完整性管理

  数据的传输有非常多潜在的风险,比如如果一个数据可以随意访问,那么它很容易被窃取;另外,如果碰到供电不稳定等问题,数据很有可能会被损坏,这时授权和完整性管理模块就需要介入,处理这些问题,确保数据的安全性。

  • 事务管理

  事务是数据库领域中极其重要的一个概念,它指数据库进行的一次完整合法的操作,而为了保证它的原子性,他需要事务管理系统做相关的备份和恢复操作。同时,要实现多个事务的并发执行,还需要事务管理系统做相关的调度和协调。

询问处理模块

  作为处理询问的编程模块,询问处理模块需要编译用户提供的数据库语句。因此,针对不同类型的数据库语句,比如数据定义语句和数据处理语句,需要提供不同的编译器。同时,有时用户提供的语句会带有冗余,可以进行一定的优化,最后根据优化后的逻辑,选择最佳的算法,调用存储模块的接口来执行相应的查询处理算法。

事务管理模块

  此处的事务管理不同于存储管理模块中的事务管理,它处理的不是硬件之间的事务,而是更高一层语句层面的事务。功能则大同小异,也是为了保障原子性、完整性而需要提供回复模块和并发控制模块。

数据库使用者

  数据库的使用者也会根据应用场景分成不同的角色。在视图模式层面,有编写应用程序的程序员和直接使用的用户,在逻辑模式层面,数据分析者高级用户会直接调用数据库的语句来对数据进行直接处理。在物理模式层面,数据库管理员会修改数据库的相关参数,尽量让数据库达到最好的性能。

数据库领域的历史

关键领域的发展

  在20世纪50年代,计算机领域的数据存储使用的是纸带,用打孔与否来表示0和1。然而物理的打孔很容易出错,难保存,数据量大的时候需要很大的成本。

  20世纪60年代,出现了磁带,它可以将数据长期地存储在磁带上,轻量便捷、容易保存。同时,网络和数据结构的发展正在将理论不断地注入应用层面。

  1961年,数据库之父,Charles W. Bachman,在提出了集成数据库的概念,数据库初具雏形。他因此获得了1973年的图灵奖。

  70年代,数据库在联机事务处理的商务应用越加广泛,一种高效的数据存储方式也就成了刚需。在1970年,E.F. Codd,在论文《A Relational Model of Data for Large Shared Data Banks》中,提出了关系模型的概念,奠定了关系数据库的基础,他也因此获得了1981年的图灵奖。

  有了坚实的理论基础,数据库应用也开始遍地开花。1974年,IBM公司的Jim Gray带领研发了第一个商用关系数据库,名为System R。同年,由UC Berkeley的Michael Stonebraker牵头,也实现了一个关系数据库,名为Ingres。他们也因为对数据库领域的卓越成就获得了图灵奖(1998年,2014年)。

技术栈时间线

  • 20世纪80年代

    • RDBMS广泛应用(R - Relational, DBMS - Database Management System)
    • 关系型数据库系统的商业应用
    • 1983年,Oracle公司发布Oracle 1.0
    • 1983年,IBM公司发布DB2
    • 1985年,IBM公司发布Informix
    • 1987年,Sybase公司发布Sybase
    • 1989年,Postgres公司发布Postgresql
    • 并行式数据库系统
    • 分布式数据库系统
    • 面向对象数据库系统
    • 对象-关系型数据库系统
    • 数据库系统在工程领域的应用
  • 20世纪90年代

    • 商业智能
    • 大型决策支持应用
    • 数据挖掘应用
    • TB级别的大数据仓库
    • OLAP(联机分析处理)
    • 电商兴起
  • 21世纪00年代

    • 网络时代
    • 大数据
    • NoSQL - Not only SQL
    • XML 和 XQuery 标准
    • 自动化数据库管理

什么是NoSQL

  NoSQL不是一个具体的SQL应用,而是非关系型数据库的总称。它没有使用传统的关系型数据模型,而是使用更加自由的模型,来达到更高的扩展性和可用性。   NoSQL数据库多应用在以大数据为典型的大量数据上,因为它们没有特别的关系约束,不需要使用关系型的数据模型来存储。

  • 21世纪10年代
    • NewSQL
    • 云数据库
    • 人工智能+数据库

Comments