本文知识点
- 数据库设计生命周期:从规划、需求分析到物理设计与运行维护的完整流程
- ER 模型基础:实体、属性类型、联系的度与基数
- ER 图设计:局部ER图设计、全局ER图合并、冲突检查、ER图转关系模式
- ER 模型扩展:弱实体、继承层次与存储策略、知识图谱与图数据库
- 关系规范化引论:数据依赖、范式、冗余异常与模式分解
一、数据库设计生命周期
1.1 数据库设计概述
数据库与软件密不可分,数据库工程可以视为软件工程的一部分。一个数据库系统同样经历从规划、设计、实现到运行维护的完整生命周期。
数据库设计的输入是各种需求(业务需求、信息需求),经过概念设计、逻辑设计、物理设计,最终实现和运行维护。这三个层次对应数据库的三级模式结构。
1.2 规划阶段
规划阶段需要明确:
- 系统目标:数据库上面要支撑哪些信息系统、哪些功能
- 系统调查:对整个企业组织进行调查,了解企业内部有哪些数据以及数据之间的逻辑关系
企业信息系统的数据库设计需要具备前瞻性。一套数据库建好后可能支撑几十上百个微服务,且新应用还在持续开发中。数据库只有一套,但上层应用可能不断增长,因此设计阶段就必须考虑未来的扩展需求。
- 多角度评估:
- 技术角度:并发性、用户规模。少量用户(十几人)选集中式数据库即可;几百人需要分布式数据库;上万人可能需要 NoSQL 或 NewSQL
- 经济角度:国产数据库 vs 商业数据库,集中式 vs 分布式,价格差异很大
- 法律角度:隐私合规、安全合规,特别是医疗、政府、军队系统
案例(上海图书馆):上图从最初只管理老馆读者(每天几百人),扩展到统一管理全上海所有社区图书馆(上万人以上),系统的选型和设计需要随之调整。此外上图除图书外还管理大量文献(民国报纸、图片等),涉及图片存储时需考虑 NoSQL 数据库。
1.3 需求分析阶段
需求分析的目标是将业务流程梳理清楚:
- 梳理业务流程图:画出业务的实施过程,了解有哪些业务流程
- 设计数据流图(DFD):从业务流程中找出涉及哪些数据,标记数据处理过程
- 产生数据字典:定义清楚所有数据项
需求分析不要求像软件需求那样精确描述功能细节,但必须把业务流程和数据梳理清楚。
1.4 概念设计阶段
概念设计的目标是明确数据——有哪些数据、用什么字段来描述这些数据。
概念设计分两步:
- 局部 ER 图:围绕不同的用户(或不同的应用),分别设计局部 ER 图
- 全局 ER 图:将局部 ER 图合并成一张完整的 ER 图
1.5 逻辑设计阶段
将概念设计(ER 图)转化为数据库的关系表结构。
- ER 图可以直接转换得到关系模式,但这个模式可能不够好(存在冗余)
- 需要不断对模式进行优化和修正,去除冗余
- 去除冗余会导致表被拆分,进而导致查询时需要更多的连接操作(JOIN),而连接的性能较差
- 因此需要在去除冗余(数据质量) 和查询性能之间做权衡
规范化设计(第四章)的核心问题是:评价 ER 图转换得到的关系模式是否足够好,并消除其中的冗余。
1.6 物理设计阶段
决定数据怎么存——数据存放在硬盘上的具体方式:
- 表存放在哪块硬盘上(多块硬盘的情况下)
- 日志存放在哪(旧硬盘 vs 新硬盘的选择)
- 索引结构的设计(如何加速查询)
- 用户权限设置
- 安全性考虑
1.7 实现与运行维护
- 数据迁移:从老系统迁移到新系统
- 功能测试与性能测试:测试不同并发情况下的性能、每个查询的执行时间
- 日常维护:每天备份、安全性和完整性控制、性能监控
- 分库分表:随着数据积累,按年/按月将数据拆分,保证每张表不至于太大
例如,银行交易系统通常要求每次交易的响应时间不超过 3 秒。
二、ER 模型(实体联系模型)
2.1 ER 模型的发展历史
ER 模型(Entity-Relationship Model)伴随着关系数据库的诞生而出现,主要用于描述:
- 实体(Entity)
- 实体之间的联系(Relationship)
- 实体的属性(Attribute)
在关系数据库相对成熟之后,对 ER 模型的研究一度减少。但近年来,随着大数据和图数据库的兴起,基于 ER 模型的图数据结构再次受到关注。
大数据时代数据碎片化严重,而实体-关系这种描述方式非常灵活,催生了图数据库的发展。图数据库的结构和 ER 图非常相似。
2.2 属性的类型
| 属性类型 | 说明 | 示例 |
|---|---|---|
| 单值属性 | 只有一个值 | 姓名、性别 |
| 多值属性 | 可以取多个值 | 销售价格(代销价、批发价、零售价、优惠价等) |
| 复合属性 | 具有层次结构的属性 | 地址(邮政编码、省、市、区、街道、门牌号) |
| 派生属性 | 可以从其他属性计算得出 | 实发工资 = 基本工资 + 奖金 - 房租 |
设计技巧:对于多值属性(如销售价格),如果把所有价格类型都作为列放在一张表里,新增一种价格就要加一列,非常不灵活。更好的做法是把”销售价格”单独作为一个实体,包含”销售方式”和”具体价格”两个属性,然后用关联来连接。
设计技巧:对于派生属性(如实发工资),不需要一定要存放在数据库里,因为它可以从其他属性计算出来,存了反而是冗余。
2.3 联系的度(Degree)
联系按参与的实体数量分为:
| 度 | 说明 | 示例 |
|---|---|---|
| 一元联系 | 联系只涉及一个实体 | 运动员的名次顺序(前一名、后一名都是运动员) |
| 二元联系 | 联系涉及两个实体 | 教师教学生、零件由子零件组成 |
| 三元联系 | 联系涉及三个实体 | 进货(从商店买商品放入仓库) |
2.4 联系的基数(Cardinality)
| 基数类型 | 说明 | 示例 |
|---|---|---|
| 一对一(1:1) | 每个实体最多与对方一个实体关联 | 运动员的成绩排名顺序 |
| 一对多(1:N) | 一方对应多方 | 领导与职工(一个领导管多个职工) |
| 多对多(M:N) | 双方都是多对多 | 零件组成(一个零件由多个子零件构成,一个子零件参与多个零件) |
联系还可以加基数限制,例如:每个学生选课 1-6 门,每门课不超过 50 个学生。
2.5 设计中的实用技巧
分离常变属性与不变属性
对一个实体,常变的属性(如职务、工资、奖金)和不变的属性(如姓名、出生日期)可以分开存储。
这样做的原因有两个:一是方便管理,二是对系统效率有影响。数据库元组按行存储在硬盘上,访问一个元组时需要将整行读出。如果表中混合了大量不常变的属性,而每次仅修改变动属性,则每次都需要读出整条很长的元组,效率很低。
这个思路的极端结果就是列存储(Columnar Storage)——不是按行存,而是按列存,每一列存储在一个地方。这是大规模数据管理中非常常用的手段。
不能随意合并二元关系
两个二元关系不能随意合并成一个三元关系。在二元关系中 A 和 B 之间没有直接关联,而在三元关系中 A 和 B 有了关联,语义发生了变化。
三、ER 图的设计流程
3.1 局部 ER 图设计
先针对系统的不同用户或不同应用,分别设计局部 ER 图。划分范围的标准是每类用户所涉及的数据范围。
设计步骤:
- 确定范围内的实体
- 定义每个实体的属性
- 定义实体之间的联系
属性分配原则:如果一个属性涉及多个实体类型,一般放在使用频率较高的实体类型上(因为高频使用的表往往会建更多索引,便于查找)。
3.2 局部 ER 图合并为全局 ER 图
以教学管理系统为例,可以围绕三个核心实体分别设计局部 ER 图:
- 教师:院系、项目、职称、工作量
- 学生:学会、班级、导师、宿舍、档案材料、社会关系
- 课程:选修学生、任课教师、教科书、教室、开课院系
合并时,通过公共节点(如教师、学生、课程、院系)将三张图连接在一起,形成完整的全局 ER 图。
3.3 合并时的冲突检查
由于局部 ER 图可能由不同人设计,合并时可能出现以下冲突:
| 冲突类型 | 说明 | 示例 |
|---|---|---|
| 属性类型冲突 | 同一属性用了不同的度量单位 | 重量:有的用公斤,有的用克 |
| 结构冲突 | 同一属性有不同的结构 | 地址:有的是一个字符串,有的分成省/市/区/街道 |
| 实体冲突 | 同一对象在不同图中作为不同类型的实体 | 有的作为实体,有的作为属性 |
| 命名冲突 | 同名异义或异名同义 | 同一对象用了不同名字 |
合并后还要消除冗余的属性。
3.4 ER 图转换为关系模式
实体转换:每个实体转换为一个关系(一张表)。
联系的转换:
| 联系类型 | 转换方法 |
|---|---|
| 二元 1:1 | 将联系信息放在任意一个实体对应的关系中 |
| 二元 1:N | 将”1”端的码和联系信息放在”N”端的关系中(如每个员工记录中添加经理工号) |
| 二元 M:N | 联系本身单独成为一个关系表,包含两端实体的码 |
| 三元联系 | 联系单独成为一个关系表,包含三个实体的码以及联系自身的属性 |
对于 1:N 的联系,将”1”端的码放在”N”端的原因是:如果反过来(在”1”端存储所有对应的”N”端),就会形成一个集合,违反关系模式中属性必须为原子值的基本原则。
例题(进货场景):仓库、商店、商品三个实体,以及它们之间的三元联系”进货”(含日期和数量属性)。转换为关系模式:
- 仓库表、商店表、商品表(每个实体各一张)
- 进货表(包含仓库ID、商店ID、商品ID、日期、数量)
3.5 第五章与第四章的关系
- 第五章(ER 图设计):解决属性存在性问题——哪些属性应该放在数据库里
- 第四章(规范化设计):解决模式好坏问题——ER 图转出的关系模式是否有冗余,如何评价和优化
四、ER 模型的扩展
4.1 弱实体(Weak Entity)
弱实体是指依赖于另一实体而存在的实体,用双线矩形框表示。
例如:
- 职工的社会关系:社会关系依赖于职工而存在。如果职工离职被删除,其社会关系也随之删除
- 顾客的通讯地址:地址依赖于顾客而存在
特征:
- 弱实体的存在依赖于其”所有者”实体
- 弱实体中包含外键指向所有者实体
- 删除所有者实体时,弱实体也应被级联删除
4.2 继承与子类/超类(ISA Hierarchy)
实体之间也可能存在继承关系——类似于面向对象中的继承概念。
例如:
1
2
3
4
5
人员(身份证号、姓名、年龄、性别)
├── 教师(职工号、职称)+ 继承人员的属性
└── 学生(学号、专业)+ 继承人员的属性
├── 本科生(入学年份)+ 继承学生和人员的属性
└── 研究生(导师、研究方向)+ 继承学生和人员的属性
两种存储策略
| 策略 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 向上存储 | 每层实体只存自己特有的属性,继承的属性存在父类表中 | 组织简单,访问哪层就访问哪层的表 | 连接多(查询一个本科生需要 JOIN 人员、学生、本科生三张表) |
| 向下存储 | 所有属性都存放在叶子节点对应的表中,父类不单独存数据 | 减少连接代价 | 表列会变多,访问量大 |
4.3 ER 模型在大数据与 AI 时代的新发展
从文本到知识图谱
随着深度学习和大模型技术的成熟,可以从文本中自动提取实体和关系,将一篇文本转化为图结构:
例如一段关于川普和抗议活动的文本,可提取为:
- 实体:抗议活动、华盛顿特区、川普、美国、总统
- 关系:发生在(抗议活动→华盛顿特区)、针对(抗议活动→川普)、是(川普→总统)、属于(总统→美国)
将文本转化为图结构后放在数据库中,就可以用数据库高效地做各种查找,成本远低于每次都调大模型。
图数据库(Graph Database)
图数据库将数据存放在图的节点和边中:
- 节点存放实体信息
- 边存放实体之间的关系
目前常见的图数据库:
- Neo4j(最有名的开源图数据库)
- GDB(阿里)
- TGDB(腾讯)
大多数国产数据库厂商的配置是:关系数据库 + 图数据库 + 向量数据库(有的还会有时序数据库)。
图数据库的查询语言
- Cypher(Neo4j)
- GQL(正在形成的国际标准)
- SPARQL(用于 RDF 格式的数据)
这些查询语言正在逐步统一,类似于关系数据库中 SQL 统一的趋势。
图数据库的查询类型
除了对节点属性的筛选查询外,图数据库还支持:
| 查询类型 | 说明 |
|---|---|
| 子图匹配 | 给出一个模式图,找到数据库中所有匹配的子图 |
| 连通性判断 | 判断两个节点之间是否存在路径 |
| 路径查找 | 按指定的路径模式,找两点之间的所有路径 |
图数据库的挑战
- 计算量大:子图匹配是 NP-Complete 问题,最短路径至少是 $O(n^2)$
- 性能问题:查询涉及大量随机访问,速度较慢
例如,在银行反诈系统中,诈骗资金在账户之间的流转会形成特定的图模式。系统需要实时判断此类模式是否出现,但图模式匹配的效率仍然不足,目前实际中更多采用离线方法进行判断。
图嵌入与图神经网络
为解决图查询的性能问题:
- 图嵌入(Graph Embedding):将图中的每个节点转化为一个向量
- 图和图之间的路径查找变成了向量计算,一定程度上提高了效率
但仍有挑战:
- 图嵌入通常在静态数据集上计算,而数据库是动态场景
- 向量计算可能有误差
- 新节点加入时向量的计算方式
- 这些技术如何应用到数据库系统中仍有大量问题需要研究
五、关系数据库规范化设计引论(第四章概述)
5.1 第四章的核心三要素
- 数据依赖(Data Dependency):属性和属性之间的相关性,研究冗余是如何产生的
- 范式(Normal Forms):评判模式好坏的若干标准,去除冗余以符合标准
- 模式设计方法:根据函数依赖和目标范式,自动将关系模式转化为符合范式要求的模式
三者的关系:数据依赖研究属性间的语义关联 → 范式定义标准 → 模式设计方法实现自动化转换。
5.2 关系模式的内涵(Intension)与外延(Extension)
| 概念 | 含义 | 说明 |
|---|---|---|
| 内涵 | 关系模式本身 | 表结构定义、完整性约束、属性间的语义关系、数据依赖 |
| 外延 | 关系模式的实例 | 具体的数据库表(如”计算机系学生表”“物理系学生表”),表结构和约束都一样,只是数据不同 |
在证明模式性质时,常用的一种思路是:如果一个性质在该模式的所有实例(所有外延)上都成立,那么该性质在该模式(内涵)上也成立。
5.3 冗余导致的数据异常
以一个设计不当的表为例(包含教师姓名、地址、所上课程):
| 姓名 | 地址 | 课程 |
|---|---|---|
| T1 | A1 | C1 |
| T1 | A1 | C2 |
| T1 | A1 | C3 |
| T2 | A2 | C4 |
| T2 | A2 | C5 |
T1 上了三门课,地址 A1 出现了三次——这就是冗余。
冗余导致的三种异常:
- 删除异常:T3 老师这学期不上课,删除其课程记录时,T3 的地址信息也被意外删除了
- 插入异常:T2 老师又上了一门新课,但春节期间他搬家了,插入新课记录时填入新地址,导致 T2 出现两个不同的地址(A2 和新地址),无法判断哪个是正确的
- 修改异常:T1 老师搬家了,需要修改地址。由于 T1 有 3 条记录,如果只按课程去修改(如”修改 C3 这门课老师的地址”),只会修改一条,造成数据不一致
5.4 解决方法:分解
将上述一张表拆分为两张:
- 教师地址表(姓名、地址)
- 教师课程表(姓名、课程)
分解后,上述三种异常全部消除。
冗余消除的核心原则是避免同一属性出现在多张表中,其核心方法是分表——将一张表拆分为两张或更多。
5.5 非形式化设计指南(经验性方法)
当对表信息掌握不够全面或时间紧迫时,可用以下经验性方法:
- 每张表只包含有直接联系的属性:一张表围绕一个主题设计。不要把地址信息和课程信息这两个无关主题的数据放在一起
- 避免插入、删除、修改异常:确保数据操作不会产生异常
- 避免放置经常为空的属性:如把多种价格作为列,很多列会为空
- 尽可能使等值连接在主键和外键属性上进行:系统会为主键构造索引,在主键/外键上做连接效率较高
实际案例:在学校系统中,学生既有学号又有身份证号,都可以唯一标识一个人,但通常选学号(工号)作为主键而非身份证号——因为工号在学校系统中使用更频繁,作为主键更自然。
5.6 符号约定
- 单个属性:大写字母 A, B, C, …
- 属性集:大写字母 U, V, W, …
- 关系模式:大写 R, S, T, …
- 关系实例:小写 r, s, t, …
5.7 第四章内容预告
第四章分两部分:
- 函数依赖、模式分解特性、关系模式范式
- 多值依赖及对应的分解方法