数据库基础(数据库的由来 + 第一、二、三范式)

一、数据库的产生原理

1.1 无纸化办公

将数据存在文件中。
在这里插入图片描述

1.2 李氏查询—SQL

所有计算机的问题都可以通过增加一个中间层来解决。

中间层上要有逻辑的数据结构。

学生信息:[学号,姓名,性别,入学日期,班级,地址] 

课程信息:[课程号,课程名,授课老师] 

选课 :[学号,课程号,成绩]

小李决定把这些东西称为“” ,其中的每一项称为“列”/“字段”/“属性”每一列都有类型,例如字符型,日期型,数字型等等。

查询的话是用类似这样进行的: 
SELECT  学号,姓名 
FROM 学生信息 
WHERE  入学日期='1991-9-1'

想把几个表连接起来查询也可以:
SELECT 学号,姓名, 课程名,成绩
FROM 学生信息 s , 课程信息 c, 选课 sc
ON s.学号=sc.学号 AND c.课程号=sc.课程号 
WHERE   课程名='操作系统'  AND 成绩<60 

很明显小李需要写一个解析器把这样的语句变成内部对文件的操作, 还好小李已经有一点编译原理的基础了, 努力一下还是能写出来的。

在这里插入图片描述
小李得意的把这套查询称为“李氏查询” , 李氏查询用起来简便快捷, 最大的好处是用户完全不用考虑物理层的那些文件的结构,只需要关注逻辑层的“”就可以了。—— 其实李氏查询就是SQL

有了一个中间的逻辑层,还带来了一个额外的好处:现在小李可以对物理层的文件存储进行一些优化。为了加快访问速度,小李不再采用简单的逗号分隔的文件,还增加了索引、缓存、查询优化等手段。

1.3 并发访问
  1. 原来单机的软件纷纷转为支持网络访问的系统
  2. 不仅仅是重构, 还包含了重要的功能增强:网络访问, 从单机软件变成了**客户端-服务器结构(C/S)**的软件。

在这里插入图片描述
3. 把基于文件的操作改变成基于行的操作: 每个人的修改只影响这一行
4. 遇到两个人对同一行的修改,解决办法为:给这一行加锁, 在刘老师读取了1000元, 扣除300元,并且把700 写回到数据库之前, 不允许金老师操作,这样就不会乱掉了。

1.4 原子性问题(事务、Undo日志、操作的幂等性)
  1. 转账这个操作,必须得是原子的: 要么全部发生, 要么根本不发生
  2. 把类似这样的操作叫做“事务”
  3. 做真正的操作之前,先把要做的事记录下来形成日志(Log),这个日志中包括修改的数据项标识, 数据项的旧值(修改前的值)和新值(修改后的值), 然后再进行真正的数据库修改
  4. 刚开始的时候事务处于活动状态, 只有所有的操作都正确无误的写入了磁盘,才会进入提交状态, 否则就要回滚修改。

除了原子性之外,事务还有持久性,隔离性,一致性
详情见:数据库的事务四大原则-ACID

关于“事务”,用“Undo日志”的方式来实现它。

比如旺财有200块钱, 小强有50 块钱,现在旺财要给小强转账,假设转100块。怎么实现要么不做,要么全做的?

执行真正的操作之前,要先记录数据项原来的值。拿转账这个例子,把这个事务命名为T1,在做真正的转账之前,一定要在我的日志文件中记录下事务开始之前的他俩账号余额

[事务T1,  旺财原有余额 , 200]
[事务T1, 小强原有余额, 50 ] 

如果事务执行到一半,就断电了,那数据库重启以后我就根据undo的日志文件来恢复

“嗯,那要是系统恢复的过程中又断电了,还得再次恢复,那数据岂不变得一团糟? ” CPU阿甘对断电心有余悸。

“你们仔细想想,即使我把旺财的余额和小强的余额恢复了100次,会有什么结果?”

“如果每次都试图把旺财的余额设为200, 小强余额设为50, 做多少次都没问题, 因为他俩原来的余额就是那么多 !” Tomcat恍然大悟。

“这就叫做操作的幂等性,知道不? 我可以一直做恢复,恢复过程中断电也不怕,只要把恢复做完就行。” 老头儿看到时机一到,立刻上升为理论。

“恢复数据的时候, 那你怎么才能知道一个事务没有完成呢?” Tomcat接着问道。

[开始事务 T1]
[事务T1, 旺财原有余额,200]
[事务T1, 小强原有余额,50]
[提交事务 T1]

“Undo日志文件中不仅仅只有余额, 事务的开始和结束也会记录,如果我在日志文件中看到了[提交事务 T1], 或者 [回滚事务 T1], 我就知道这个事务已经结束,不用再去理会它了, 更不用去恢复。 如果我只看到 [开始事务 T1], 而找不到提交或回滚,那我就得恢复。比如下面这个:

[开始事务 T1]
[事务T1, 旺财原有余额,200]
[事务T1, 小强原有余额,50]

“特别是,” 老头补充道, “ 我恢复以后, 需要在日志文件中加上一行 [回滚事务 T1] , 这样下一次恢复我就不用再考虑T1这个事务了。”

“不对吧, 你这个Undo日志文件会面临和数据文件一样的问题, 都是需要加载到内存才能读写, 要不然会太慢。 那要是连日志文件还没写好就断电了,那不还是玩完?” Ngnix 目光如炬,向深层次挖掘。

这是个绝佳的问题,大家纷纷把目光杀向数据库老头儿,希望这一次能把他打翻在地。
其实很简单,只需要遵循两条简单的规则就可:

  1. 在你把最新余额写入硬盘之前, 一定要先把相关的Undo日志记录写入硬盘。 例如[事务T1, 旺财原有余额,200] 一定要在旺财的新余额=100写入硬盘之前写入。
  2. [提交事务 T1] 这样的Undo日志记录一定要在所有的新余额写入硬盘之后再写入。
1.5 安全
  1. 添加权限控制
赶紧加上一个权限系统, 小李想了想,  先定义三大类权限:
1. 对数据操作的, 例如SELECT, UPDATE, INSERT等
2. 对结构操作的, 例如创建表,修改表,等
3. 做管理的, 例如备份数据, 创建用户等
然后就可以把这些权限授予某个用户了, 很多时候,还需要把表附加上, 像这样:
GRANT  SELECT on 财务表 to  系主任
GRANT  CREATE_TABLE to 张老师

至此。这个系统的中间层完全可以剥离出来,形成一个完整的软件了, 小李把它称为:数据库
在这里插入图片描述


二、第一范式、第二范式、第三范式

  1. 关系数据库最忌讳的就是在一个单元格里存储多个值 —— 不满足第一范式的数据库就不是关系数据库
2.1 第一范式(1NF)

第一范式是指数据库表中的每一列都是不可分割的基本数据项,强调列的原子性同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。

说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。

例如,由“职工号”“姓名”“电话号码”组成的表(一个人可能有一部办公电话和一部移动电话),这时将其规范化为1NF可以将电话号码分为“办公电话”和“移动电话”两个属性,即职工(职工号,姓名,办公电话,移动电话)。

2.2 第二范式(2NF)

第二范式建立在第一范式的基础上,即满足第二范式一定满足第一范式,第二范式要求数据表每一个实例或者行必须被唯一标识。
除满足第一范式外还有两个条件,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。(所有属性仅仅依赖于主键的情况)

例如,在选课关系表(学号,课程号,成绩,学分),关键字为组合关键字(学号,课程号),但由于非主属性学分仅依赖于课程号对关键字(学号,课程号)只是部分依赖,而不是完全依赖,因此此种方式会导致数据冗余以及更新异常等问题。
解决办法是将其拆分为两个关系模式:学生表(学号,课程号,分数)和课程表(课程号,学分),新关系通过学生表中的外关键字课程号联系,在需要时进行连接。

2.3 第三范式(3NF)

如果关系模型R是第二范式,且每个非主属性都不传递依赖于R的候选键,则称R是第三范式的模式。(没有传递依赖)
在这里插入图片描述
上表存在:订单号能决定用户ID,而用户ID 能决定用户名称。即,存在传递依赖: 订单号->用户ID->用户名称

此时用户信息无法单独管理,所以继续拆分:
在这里插入图片描述
在这里插入图片描述
拆分完就没有传递依赖了, 我们可以称之为 第三范式 了。
在这里插入图片描述

把表不断地拆分后,把这些“分散表”连接(Join)起来才能形成最初的那张表。如果在数据量特别巨大的时候,这种连接很耗时。所以在实践中有时候不得不违反范式违反第三范式),做点数据的冗余


参考文章:

  1. 数据库-第一范式、第二范式、第三范式、BC范式、第四范式简析
  2. [数据库] 第一范式、第二范式、第三范式、BC范式
  3. 《码农翻身》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章