前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么我不喜欢三范式

为什么我不喜欢三范式

作者头像
美码师
发布于 2019-08-21 07:55:53
发布于 2019-08-21 07:55:53
76300
代码可运行
举报
文章被收录于专栏:美码师美码师
运行总次数:0
代码可运行

插曲

最近,一个远房亲戚的小表弟准备选修专业 找到我问:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"哥,现在学数据库有没有前途阿?"

"当然有啊,前途大大的呢"
"那我现在开始学数据库,需要先从什么开始呢?"

"学课程的话,先了解下数据库三范式,SQL这些吧"

"SQL我大概知道,数据库三范式是什么?"
"阿...三范式就是表的主键...唯一性那些东西吧,...嗯,应该就是那些"

"什么是主键?"

"额.....表弟你不要再问了啦,好好去百度一下行不。"

"噢...."

挂完电话,我舒了口气,由于差点暴露自己已经不记得三范式了这个不争的事实,我悄悄打开了谷歌....

数据库的这个三范式的概念,相信大多数人都不会陌生,从懵懵懂懂的大学时代就已经普及到教材了(没记错的话应该在数据库系统概论这本教材里)。还记得那会刚开始找实习的时候,由于自己本事太小,连简历都不知道怎么写好,尤其是擅长技术的部分更是一片空白。于是乎会找来隔壁几个学霸的简历来做参考,那会儿大家的简历上都会赫赫写着:

熟练掌握数据库三范式,精通数据库系统开发语言

又或者是:

熟悉ER图制作工具,能实现满足三范式的数据库设计

一开始觉得数据库三范式确实是个好东西,以至于面试的时候技术官没有提问到三范式的细节,自己感到了惊讶和茫然。随着工作经验逐渐见长,数据库范式理论在脑海里的强印象渐渐消除。我在想,要么是记忆的衰退,要么就是有些原则已经形成了本能的经验了。

那么,什么是数据库的范式?

三范式的定义

这里,不想花太多的篇幅去讨论理论性的东西,这些信息一抓一大把。我们就通过一些简单的例子来体会一下。

1. 第一范式

假设有一张用户信息表,上面除了用户编号、姓名之外,还会记录地址信息:

编号

姓名

性别

所在地

0001

张三

广东省,深圳市

0002

李四

海南省,海口市

在这里面,地址信息一栏就是不符合第一范式(1NF)的:

第一范式(1NF):数据库表的每一列都是不可分割的原子项

因此,应该拆分为:

编号

姓名

性别

所在省

地市

0001

张三

广东省

深圳市

0002

李四

海南省

海口市

2. 第二范式

以一个订单表为例,通常在淘宝上下单时会产生包含多个商品的订单,如下:

订单号

商品号

商品名称

价格

o1

g1

洗衣液

23

o1

g2

吹风机

125

o1

g3

蚕豆

5

o2

g9

被子

302

o2

g8

枕头

69

这里同样违反了第二范式的定义:

第二范式(2NF):每个表必须有且仅有一个数据元素为主键(Primary key),其他属性需完全依赖于主键

第二范式需建立在满足第一范式的基础之上

第二范式首先要求的是存在一个唯一的主键,在上面的表中,就必须将 订单号、商品号 作为一个联合的主键才能满足要求。那么对于第二点要求呢? 其他属性是否依赖于这个主键?在订单的场景中,我们可以认为这算是合理的,因为商品的价格甚至名称都可能会发生变化,而在每个订单中所看到的这些信息都应该是不变的, 谁也不希望看到自己已经支付的订单中的商品信息突然大降价.. 当然更重要的还是保持订单总价与商品单价记录的一致性。因此这里的记录可以认为是商品信息在创建订单时的一个快照。

但是,对于下面的这一场景可能就不合适了:

订单号

商品号

商品名称

价格

商品类别

o1

g1

洗衣液

23

家居

o1

g2

吹风机

125

电器

o1

g3

蚕豆

5

食品

o2

g9

被子

302

家居

o2

g8

枕头

69

家居

商品所属的类别一般是固定的,也就是商品的类别属性仅仅与商品编号相关,即仅仅是依赖于主键的一部分。这就违反了第二范式中"其他属性必须完全依赖于主键"的规则,因此需要将该属性分离到商品信息表中。

3. 第三范式

让我们回到一开始的用户表,如果在用户信息表中,同时补充一些城市的信息:

编号

姓名

性别

城市

城市特色

城市人口

0001

张三

深圳市

科技、创新

1300W

0002

李四

海口市

旅游、观光

230W

这样便违反了第三范式的定义:

第三范式(3NF):数据表中的每一列都和主键直接相关,而不能间接相关

同样,第三范式也需要建立在第二范式的基础之上

很明显,这里的城市人口、特色等属性都仅仅依赖于用户所在的城市,而不是用户,只能算间接的关系。因此最好的做法是将城市相关的属性分离到一个城市信息表中。

为什么需要范式

数据库范式为数据库的设计、开发提供了一个可参考的典范,在许多教学材料中也是作为关键的课程内容。那么范式的提出是为了解决什么问题?

  • 第一范式,要求将列尽可能最小的分割,希望消除某个列存储多个值的冗余的行为。比如用户表中的地址信息,拆分为省、市这种明确的字段,可以按独立的字段检索、查询。
  • 第二范式,要求唯一的主键,且不存在对主键的部分依赖,希望消除表中存在冗余(多余)的列。比如订单表中的商品分类、详情信息,只需要由商品信息表存储一份即可。
  • 第三范式,要求没有间接依赖于主键的列,即仍然是希望消除表中冗余的列。比如用户表中不需要存储额外的 其所在城市的人口、城市特点等信息。

很明显,这些范式大都是为了消除冗余而提出的,这有利于数据的一致性,当然也可以尽可能的减少存储成本。

PS:你懂得三范式,可以帮老板省钱,难怪简历上要写上..

除了本文中提到的三范式之外,实质上还有BCNF范式、第四、第五范式。

借助三范式的理念,你可以设计出很精炼的数据库表结构。然而现有的项目应用并不会完全遵循范式的理念,原因比如:

  • 性能原因,没有任何冗余的表设计会产生更多的查询行为,这意味着会产生更多次的数据库IO操作。在一些实时交互的系统中,可能会慢得让人难以忍受。当然,你可以使用数据库的 连接(join) 操作,而事实上数据库提供 join 也就是为了来缓解这种问题。但一旦用到了分库分表方案,这个问题就会非常的棘手。
  • 成本结构的变化,数据库范式是在20世纪提出的,当时的磁盘存储成本还很高。随着科技发展,数据存储的成本已经大幅度缩减,对于采用范式设计(规避冗余)带来的成本缩减收益已经不那么明显。

反范式设计

既然范式是为了消除冗余,那么反范式就是通过增加冗余、聚合的手段来提升性能。比如,为了提升查询的性能,在CMS的文章表中同时冗余作者的信息。冗余的做法会牺牲一定的数据一致性,或是带来数据同步的麻烦,但通常也是需要业务上进行权衡取舍。

当然,除了冗余(存储多份拷贝) 之外,还有另外的理念,即数据的聚合,或者叫嵌套。这种做法相当于是将多个字段(列)合并存储到数据库表的一个列中。

比如一条订单数据就可以同时包含许多信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
 "oid": "0001",
 "price": {
  "total": 380,
  "benefit": 40
 },

 "goods": [{
   "gid": "SN001",
   "name": "蓝月亮洗衣液",
   "price": 41,
   "amount": 2
  },
  {
   "gid": "SN003",
   "name": "电动剃须刀",
   "price": 99,
   "amount": 1
  }
 ],

 "address": {
  "contact": "张三",
  "phone": "150899000"
   ...
 }
...
}

这种灵活的结构几乎是 NoSQL 的专利,比如 MongoDB 文档数据库就可以直接以内嵌数组、对象的形式来实现聚合式存储,这无疑带来了极大的灵活性。而 MySQL 在5.2.7版本开始支持JSON结构化列,也进入了聚合式存储的队伍,与其对标的PostGreSQL 则是9.4版本就已经支持。

反范式的做法在互联网项目、开源产品中也比较常见,比如Discuz 的数据表设计中就存在许多的冗余列、聚合字段。一方面,除了能获得性能的提升之外,数据压缩、高度灵活扩展(非结构化) 也是反范式设计能获得青睐的理由。

当然,这里并非一律反对数据库范式,理解范式仍然是做好数据库设计的一门基础,比如选择合适的主键、清晰的划分每一列属性等等。在项目中仍然需要根据自身的业务特点在范式和反范式中找到平衡点(通常是两者的结合)。类似于架构设计中空间换时间的一些做法,这其中涉及到的各种取舍都是需要经过权衡的。

也可以说这是一门“艺术“,因为没有标准答案...

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 美码师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Note_Spark_Day02:Standalone集群模式和使用IDEA开发应用程序
可以发现在一个Spark Application中,包含多个Job,每个Job有多个Stage组成,每个Job执行按照DAG图进行的。
Maynor
2021/12/07
4740
Note_Spark_Day02:Standalone集群模式和使用IDEA开发应用程序
Note_Spark_Day01:Spark 框架概述和Spark 快速入门
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4yHXs6Sp-1627098349792)(/img/image-20210419160056620.png)]
Maynor
2021/12/07
8670
Note_Spark_Day01:Spark 框架概述和Spark 快速入门
Note_Spark_Day01:Spark 基础环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Rk9bK5g-1625406507847)(/img/image-20210419160056620.png)]
Maynor
2021/12/07
6500
Note_Spark_Day01:Spark 基础环境
手把手教你大数据离线综合实战 ETL+Hive+Mysql+Spark
两个主要方面的业务: ⚫ 第一个、数据【ETL 处理】 ◼依据IP地址,调用第三方库解析为省份province和城市city; ◼将ETL后数据保存至PARQUET文件(分区)或Hive 分区表中; ⚫ 第二个、数据【业务报表】 ◼读取Hive Table中广告数据,按照业务报表需求统计分析,使用DSL编程或SQL编程; ◼将业务报表数据最终存储MySQL Table表中,便于前端展示; 上述两个业务功能的实现,使用SparkSQL进行完成,最终使用Oozie和Hue进行可视化操作调用程序ETL和Report自动执行。
Maynor
2021/12/07
1.6K1
手把手教你大数据离线综合实战 ETL+Hive+Mysql+Spark
Spark案例库V1.0版
基于Spark框架使用Scala语言编程实现词频统计WordCount程序,将符号数据过滤,并统计出现的次数
Maynor
2021/12/07
1.2K0
从0-1搭建Spark本地开发环境(idea)
https://www.scala-lang.org/download/2.11.12.html
Eights
2020/07/10
3.4K0
从0-1搭建Spark本地开发环境(idea)
Spark Day06:Spark Core之Spark 内核调度和SparkSQL快速入门
ShuffleMapTask要进行Shuffle,ResultTask负责返回计算结果,一个Job中只有最后的Stage采用ResultTask,其他的均为ShuffleMapTask。
Maynor
2022/02/17
8960
Spark Day06:Spark Core之Spark 内核调度和SparkSQL快速入门
2021年大数据Spark(五):大环境搭建本地模式 Local
Local模式就是,以一个JVM进程,去模拟整个Spark的运行环境,就是讲Master和Worker角色以线程的形式运行在这个进程中。
Lansonli
2021/10/09
1.1K0
【Spark】 Spark的基础环境 Day02
​ 当Spark Application运行到YARN上时,在提交应用时指定master为yarn即可,同时需要告知YARN集群配置信息(比如ResourceManager地址信息),此外需要监控Spark Application,配置历史服务器相关属性。
Maynor
2021/12/07
3790
【Spark】 Spark的基础环境 Day02
Spark Day05:Spark Core之Sougou日志分析、外部数据源和共享变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRu202yb-1644834575572)(/img/image-20210423150750606.png)]
Maynor
2022/02/17
1.1K1
Spark Day05:Spark Core之Sougou日志分析、外部数据源和共享变量
Note_Spark_Day07:Spark SQL(DataFrame是什么和数据分析(案例讲解))
使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行针对性的优化,最终达到大幅提升运行时效率
Maynor
2021/12/07
2.5K0
Note_Spark_Day07:Spark SQL(DataFrame是什么和数据分析(案例讲解))
【精通Spark系列】万事开头难?本篇文章让你轻松入门Spark
Apache Spark是专门为大规模数据处理而设计出来的计算引擎,相对于Hadoop MapReduce将结果保存在磁盘中,Spark使用了内存保存中间结果,能在数据尚未写入磁盘时在内存中进行运算。Spark只是一个计算框架,不像Hadoop一样包含了分布式文件系统和完备的调度系统,如果需要使用Spark,需要搭载其他文件系统例如用HDFS和更成熟的调度系统进行配合更好的进行计算工作。
大数据小禅
2021/12/06
4290
【精通Spark系列】万事开头难?本篇文章让你轻松入门Spark
2021年大数据Spark(十一):应用开发基于IDEA集成环境
实际开发Spark 应用程序使用IDEA集成开发环境,Spark课程所有代码均使用Scala语言开发,利用函数式编程分析处理数据,更加清晰简洁。
Lansonli
2021/10/09
1.1K0
【Spark Streaming】Spark Day11:Spark Streaming 学习笔记
将每批次数据状态,按照Key与以前状态,使用定义函数【updateFunc】进行更新,示意图如下:
Maynor
2021/12/06
1.2K0
【Spark Streaming】Spark Day11:Spark Streaming 学习笔记
Spark Core入门1【Spark集群安装、高可用、任务执行流程、使用Scala/Java/Lambda编写Spark WordCount】
Spark是一种快速、通用、可扩展的大数据分析引擎,包含SparkSQL、Spark Streaming、GraphX、MLlib等子项目。
Java架构师必看
2021/05/14
1.6K0
Spark Core入门1【Spark集群安装、高可用、任务执行流程、使用Scala/Java/Lambda编写Spark WordCount】
用java程序完成从kafka队列读取消息到sparkstreaming再从sparkstreaming里把数据导入mysql中
有一段时间没好好写博客了,因为一直在做一个比较小型的工程项目,也常常用在企业里,就是将流式数据处理收集,再将这些流式数据进行一些计算以后再保存在mysql上,这是一套比较完整的流程,并且可以从数据库中的数据再导入到hadoop上,再在hadoop上进行离线较慢的mapreduce计算,这是我后面要进行的项目。
gzq大数据
2020/11/26
1.1K0
用java程序完成从kafka队列读取消息到sparkstreaming再从sparkstreaming里把数据导入mysql中
spark 入门_新手入门
Spark是一种快速、通用、可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMPLab,2010年开源,2013年6月成为Apache孵化项目,2014年2月成为Apache顶级项目。项目是用Scala进行编写。
全栈程序员站长
2022/11/01
1.1K0
spark 入门_新手入门
惊了!10万字的Spark全文!
Hello,大家好,这里是857技术社区,我是社区创始人之一,以后会持续给大家更新大数据各组件的合集内容,路过给个关注吧!!!
刘浩的BigDataPath
2022/11/30
1.6K0
Spark2.0学习(二)--------RDD详解
添加针对scala文件的编译插件 ------------------------------ <?xml version="1.0" encoding="UTF-8"?> <project xml
大数据流动
2019/08/08
7460
【Spark Streaming】Spark Day10:Spark Streaming 学习笔记
首先,学习SparkStreaming流式计算模块,以批处理思想处理流式数据,进行实时分析。
Maynor
2021/12/06
1.2K0
【Spark Streaming】Spark Day10:Spark Streaming 学习笔记
推荐阅读
相关推荐
Note_Spark_Day02:Standalone集群模式和使用IDEA开发应用程序
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验