数据库的三大范式
第一范式(1NF):指的是数据库表中的任何属性都具有原子性,不可再分解;
第二范式(2NF):指的是对记录的唯一性约束,要求记录要有唯一标识,即实体的唯一性;
第三范式(3NF):指的是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。
理解思路:
(一、第一范式(1NF):第一列都是不可分割的原子数据数据项);
所谓第一范式(1NF)是指在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
在任何一个关系数据库中,第一范式(1NF)是对关系模式的设计基本要求,一般设计中都必须满足第一范式(1NF)。不过有些关系模型中突破了1NF的限制,这种称为非1NF的关系模型。换句话说,是否必须满足1NF的最低要求,主要依赖于所使用的关系模型。
特点:
- 属性不可分割,即每个属性都是不可分割的原子项。(实体的属性即表中的列)
(二、第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖);
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
特点:
- 满足第一范式;
- 第二范式需要确保数据表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言),每张表只描述一件事情;
- 消除部分依赖,要求一张表中的每一列都完全依赖于主键(针对于组合主键),也就是不会出现某一列只和部分主键相关。
五个概念:
①函数依赖:A–>B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A;
例如:学号–>姓名、(学号,课程名称)–>分数;
②完全函数依赖:A–>B,如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值;
例如:(学号,课程名称)–>分数;
③部分函数依赖:A–>B,如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称)–>姓名;
④传递函数依赖:A–>B,B–>C,如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称C传递函数依赖于A;
例如:学号–>系名、系名–>系主任;
⑤码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码;
例如:该表中码为(学号,课程名称)
- 主属性:码属性组中的所有属性;
- 非主属性:除过码属性组的属性;
(三、第三范式(3NF):在2NF的基础上,任何非主属性不依赖于其他非主属性(在2NF基础上消除传递依赖))。
第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。
特点:
- 前提:满足第一范式和第二范式;
- 第三范式需要确保数据表中的每一列数据表和主键直接相关,而不能间接相关;
- 消除传递依赖,要求一张表中的每一列都和主键是直接依赖的,不是间接依赖。
3NF基础上,任何主属性不能对主键子集依赖(在3NF基础上消除主属性对主码子集的依赖)。巴斯-科德范式(BCNF)是第三范式(3NF)的一个子集,即满足巴斯-科德范式(BCNF)必须满足第三范式(3NF)。通常情况下,巴斯-科德范式被认为没有新的设计规范加入,只是对第二范式与第三范式中设计规范要求更强,因而被认为是修正第三范式,也就是说,它事实上是对第三范式的修正,使数据库冗余度更小。这也是BCNF不被称为第四范式的原因。某些书上,根据范式要求的递增性将其称之为第四范式是不规范,也是更让人不容易理解的地方。而真正的第四范式,则是在设计规范中添加了对多值及依赖的要求。
MySQL进阶系列:数据库设计中的范式究竟该如何使用
“ 这篇文章主要为了说明规矩要遵守,但是也别这么死板,要知道因场景不同而变化。了解各自的优缺点,在不同业务中根据需求选择使用。”
我们在项目上进行数据库设计的时候要求遵守三范式,为什么会约束三范式呢:为了减少数据冗余。
回忆下是哪三范式:
- 所有属性具有原子性,列不可分割。例如家庭地址(xx省xx市xx地址),家庭地址作为字段就是非原子的,可以拆分成字段省份,城市,地址。
- 在第一范式的基础上,要求所有非主键字段完全依赖主键,不能产生部分依赖。一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
- 在第二范式的基础上,保证每列都和主键直接相关,不存在传递依赖表中的字段和主键直接对应不依靠其他中间字段。传递依赖:A—>B—>C。
优点:
- 范式化的更新通常比反范式更快。
- 当数据较好的范式化后,就只有很少或者没有重复数据。
- 范式化的表通常更小,可以更好的放进内存了,所以执行操作也会更快。
- 很少有多余的数据意味着检索列表数据时更少需要distinct和group by语句。
缺点:
- 通常需要表关联,复杂一点的查询语句可能至少需要一次关联,也可能会使得索引失效。
阿里开发手册中规定表join关联不能超过3个,主要原因就是数据量大的时候join查询非常慢,但是也不一定不能关联多个,具体问题具体分析,数据量少的时候多张表关联也没影响的。
优点:
- 数据都在一张表中,可以很好的避免关联。
如果不需要关联,则对大部分查询最差的情况—即使表没有使用索引,是全表扫描。当数据比内存大时,可能比关联要快得多,因为这样避免了随机I/O(全表扫描基本上是顺序I/O,但不是100%的和引擎有关).
2.单表可以更有效的使用索引策略。
缺点:
- 表中的冗余较多,删除数据的时候容易造成部分有用数据丢失。
实际上完全范式或者完全反范式都是理论上的。在实际的项目开发中,基本都是混用的,没有严格的规定。
案例分析:
例A: 假设有一个网站,允许用户发送消息,而且其中一些用户是VIP,现在想查看VIP用户的近10条信息。
- 完全范式化 表设计:user(user_id,user_type)表和message(message_id,user_id,message_text,published)表,published构建索引查询sql:
上面sql需要表关联,mysql需要扫描message 表的日期published的索引,对于每一行找到的数据都要到user表检索是不是VIP用户,如果VIP只是很小的一部分,这个效率就很低下了。另一种执行计划是先从user表开始,找所有VIP用户获取并排序,这种可能更糟糕。
2. 完全的反范式,需要在message表中存储user数据,就会存在message数据操作影响user数据的问题。
3. 混用范式和反范式:修改message表结构增加用户类型字段user_type, 如:message(message_id,user_id,message_text,published,user_type),这种设计可以避免完全范式化带来的表关联查询,也避免了完全反范式的插入删除问题(即使没有消息用户的信息也不会丢失)。
例B: 如果部分需求是查询的结果需要排序,从父表中冗余一些数据到子表更方便设计索引,提高查询效率。
例C: 对于缓存衍生值也是有效的,如果需要显示每个用户发了多少消息(论坛发帖),每次需要执行一个统计的自查询计算,其实可以在user表中增加消息数量的字段,当用户发送消息的时候更新这个值(需要平衡更新和查询哪个更好)。
以上只是为了说明范式和反范式以及混用范式而举的例子,但是实际开发中还是要根据业务来选择怎么使用。
在表设计中,使用范式也好,反范式也好,不应该有严格的限制,该用哪种就使用哪种或者两者结合使用。
MySQL高级相关更多内容,如锁,MVCC,读写分离,分库分表等还在持续更新中,如果有想了解的内容也可以给我留言,欢迎关注催更。
我是阿纪,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏的好文,欢迎关注,欢迎关注公众号:纪先生笔记,一起做一个持续成长的技术人。
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。