关系模式R的基本函数依赖F如下:
F={队员编号→球队名,球队名→队长名,(队员编号,比赛场次)→进球数}
其主键为(队员编号,比赛场次)。
1)R不是2NF模式的原因是队员编号→球队名,所以(队员编号,比赛场次)→球队名是一个部分函数依赖关系,将R分解成2NF如下:
RI={队员编号,球队名,队长名}
R2={球队名,比赛场次,进球数}
2)由于在R1中,主键为队员编号,所以队员编号→队长名是一个传递函数依赖,将R分解成:
R11={队员编号,球队名},R12={球队名,队长名}
则将R分解为R11,R12,R2后均为3NF的关系模式。
反范式
数据库设计要严格遵守范式,这样设计出来的数据库,虽然思路很清晰,结构也很合理,但是,有时候却要在一定程度上打破范式设计。因为范式越高,设计出来的表可能越多,关系可能越复杂,但是性能却不一定会很好,因为表一多,就增加了关联性。特别是在高可用的OLTP数据库中,这一点表现得很明显,所以就引入了反范式。
不满足范式的模型,就是反范式模型。反范式跟范式所要求的正好相反,在反范式的设计模式中,可以允许适当的数据冗余,用这个冗余可以缩短查询获取数据的时间。反范式其本质上就是用空间来换取时间,把数据冗余在多个表中,当查询时就可以减少或者避免表之间的关联。反范式技术也可以称为反规范化技术。
反范式的优点:减少了数据库查询时表之间的连接次数,可以更好地利用索引进行筛选和排序,从而减少了I/O数据量,提高了查询效率。
反范式的缺点:数据存在重复和冗余,存在部分空间浪费。另外,为了保持数据的一致性,则必须维护这部分冗余数据,因此增加了维护的复杂性。所以,在进行范式设计时,要在数据一致性与查询之间找到平衡点,因为符合业务场景的设计才是好的设计。
在RDBMS模型设计过程中,常常使用范式来约束模型,但在NoSQL模型中则大量采用反范式。常见的数据库反范式技术包括:
●增加冗余列:在多个表中保留相同的列,以减少表连接的次数。冗余法以空间换取时间,把数据冗余在多个表中,当查询时可以减少或者是避免表之间的关联。
●增加派生列:表中增加可以由本表或其他表中数据计算生成的列,减少查询时的连接操作并避免计算或使用集合函数。
●表水平分割:根据一列或多列的值将数据放到多个独立的表中,主要用于表的规模很大、表中数据相对独立或数据需要存放到多个介质的情况。
●表垂直分割:对表按列进行分割,将主键和一部分列放到一个表中,主键与其他列放到另一个表中,在查询时减少I/O次数。
举例,有学生表与课程表,假定课程表要经常被查询,而且在查询中要显示学生的姓名,则查询语句为:
SELECT CODE, NAME, SUBJECT FROM COURSE C, STUDENT S WHERE S.ID=C.CODE WHERE CODE=?
如果这个语句被大范围、高频率执行,那么可能会因为表关联造成一定程度的影响,现在,假定评估到学生改名的需求是非常少的,那么,就可以把学生姓名冗余到课程表中。注意:这里并没有省略学生表,只不过是把学生姓名冗余在了课程表中,如果万一有很少的改名需求,只要保证在课程表中改名正确即可。
那么,修改以后的语句可以简化为:
SELECT CODE, NAME, SUBJECT FROM COURSE C WHERE CODD=?
范式和反范式的对比如下表所示:
模型
|
优点
|
缺点
|
范式化模型
|
数据没有冗余,更新容易
|
当表的数量比较多,查询设计需要很多关联模型(Join)时, 会导致查询性能低下
|
反范式化 模型
|
数据冗余将带来很好的读取性能(因为不需 要Join很多表,而且通常反范式模型很少做更 新操作)
|
需要维护冗余数据,从目前NoSQL的发展可以看到,对磁盘 空间的消耗是可以接受的
|