数据库的设计规范
数据库的设计规范
1. 为什么需要数据库设计
个人观点
一个好的数据库设计,可以减轻开发的难度,提高开发效率。同时减轻后续数据库的维护工作以及良好的扩展性。总体一句话:一个好的数据库设计有诸多的好处。反之,如果数据库设计的不行,那就...
设计数据库(表)的时候,要考虑很多问题在。比如:
- 用户都需要什么数据?那些数据是需要保存到数据库(表)中的?
- 如何保证数据库(表)中数据的 正确性,在插入,删除,更新数据的时候该进行怎么样的 约束检查?
- 如何降低数据库(表)的 数据冗余度,保证数据库(表)不会因为数据量的增长而迅速扩张?
- 如何让负责数据库的维护人员 更方便 地使用数据库?
- 等 ...
注意:使用数据库的应用场景各不相同,针对不同的情况(业务),设计出来的数据表也是 千差万别。
现实情况中,面临的场景:
- 当系统运行了一段时间后,我们才发现数据表设计的有问题(相当麻烦)。此时,我们要重新调整数据表的结构,就需要做数据迁移,还有可能影响系统的正常访问。
一个 不好的数据库设计 可能会出现的问题:
- 数据冗余,数据重复,浪费存储空间
- 数据增,删,改时异常
- 无法表示正确的数据
- 丢失有效数据
- 系统性能差
一个 好的数据库设计 应该以下优点:
- 节省数据的存储空间
- 保证数据的完整性(正确性,有效性)
- 方便应用系统的开发
总之一句话,在应用系统开发之前,我们就需要重视数据表的设计。为了建立 冗余较小,结构合理 的数据库,设计数据库时页必须遵循一定的规则(范式)
2. 范式
2.1 范式简介
什么是范式:在关系型数据库中,设计数据表的基本原则,规则 就称为 "范式"。
范式能做什么:通过 遵循 "范式" 可以我们设计一个结构合理,性能较好的关系型数据库。
在设计一张数据表结构时,需要满足一定的范式级别(至少满足第三范式)。
"范式" - 详情介绍:
"范式"的英文名称是 Normal Form,简称 NF。它是英国人E.F.Cood 在上世纪70年代提出关系数据库模型后总结出来的。"范式" 是关系型数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的 规则 和 指导方法 。
2.2 范式 - 都有那些
目前关系型数据库 有六种常见范式,按照范式级别,从低到高分别是:
- 第一范式(1NF)
- 第二范式(2NF)
- 第三范式(3NF)
- 巴斯-科德范式(BCNF)
- 第四范式(4NF)
- 第五范式(又称完美范式,5NF)
注意:范式之间 "从高到低" 是存在包含关系的。例如:当在满足”第三范式“的时候,也必将满足"第一和第二范式",以此类推。那换句话说就是:如果"不满足第二范式",那"肯定也不满足第三范式以及后面的范式"。
在设计数据库时 遵循的范式越高阶,冗余度就越低,同时高阶的范式一定符合低阶范式的要求。
在实际的开发中:
一般来说,在关系型数据库设计中,最高也就遵循到 巴斯-范式(BCNF),普遍都是 第三范式(3NF)。当然这也不是绝对的,有时候为了提高某些查询性能,我们还需要破坏范式规则("反范式化")。
2.3 键和相关属性的概念
范式的定义会使用到 "主键" 和 "候选键",数据库中的 键(Key)由一个或者多个属性组成。数据表中常用的几种键和属性(字段)的定义如下:
- 超键:能够唯一标识一行数据的属性(字段)集叫做 "超键"(包含了 "主键" 的多个字段)。
- 候选键:如果 "超键" 不包括多余的属性(字段),那么这个 "超键" 就是 "候选键"(可以作为 "主键" 的字段集)。
- 主键:用户可以从 "候选键" 中选择一个作为 "主键"。(在 "主键" 字段集中选择一个作为 "主键" )
- 外键:A 表中的属性(字段)集不是 A 表的 "主键",而是另一张表的 "主键",那么这个属性(字段)就是 A 表中的 "外键"。(本表中用于存储其他表的主键的字段就是 "外键")
- 主属性:"候选键" 中的任何一个属性(字段),称为 "主属性"。("候选键" 中的 任何一个属性(字段) )
- 非主属性:与 "主属性" 相对,指的是不包含在任何一个 "候选键" 中的属性(字段)。(不能作为 "主键" 的字段)
通常,我们也将 "候选键" 称之为 "码",把 "主键" 为 "主码",因为键可能是由多个属性(字段)组成。针对单个属性(字段),我们使用 "主属性" 和 "非主属性" 来区分。
举例:有两张表
球员表:球员编号|姓名|身份证号|年龄|球队编号
球对表:球队编号|主教练|球队所在地
- 超键:对于球员表来说,只要包含了球员编号或者身份证号的任意组合,就叫超键。比如:(球员编号),(球员编号,姓名),(身份证号,年龄)等...。
- 候选键:最少的超键(可以作为主键的字段列表),球员表的候选键就是(球员编号)和(身份证号)
- 主键:从候选键中选择一个。比如(球员编号)。
- 外键:球员表中的球队编号。
- 主属性:球员表中,主属性是(球员编号),(身份证号)
- 非主属性:球员表中,非主属性是(姓名),(年龄),(球队编号)
2.4 第一范式(1NF)
第一范式(1NF) 主要是确保数据表中每个字段的值必须具有 原子性(单个值),也就是说数据表中每个字段的值为 不可再次拆分 的最小数据单元。
一句话表示:一个字段中不能存储多个值。
举例1:一张简单的表
我们在设计某个字段的时候,对于字段 X 来说,字段 X 中的值是不能再单独拆分成为多个值。
id X 1 1 2 3 4 2 5 3 6 该表不符合 第一范式(1NF),因为该表违背了 "表中每个属性(字段)都必须具有原子性(一个字段中只能存储一个值)" 的要求。id 为 1 的 X 列违反了该规则。正确的做法:
id X 1 1 2 2 3 3 4 4 5 5 举例2:一张用户表
字段名 说明 id 主键id username 用户名 password 密码 user_info 用户信息(包含真实姓名,电话,住址,性别,身高等等...) 其中,user_info 字段为用户信息,该字段中存储多个值,该字段是可以拆分成多个字段 的,不符合 第一范式(1NF)。正确的做法:
字段名 说明 id 主键id username 用户名 password 密码 user_name 用户姓名 user_phone 电话 user_address 住址 user_sex 性别 user_hight 身高 ... ...
注意:表中属性(字段)的"原子性"是 "主观的"。最终还是要根据实际的业务需求来决定的。
2.5 第二范式(2NF)
第二范式(2NF) 要求,在满足第一范式的基础上,还要 满足数据表里的每一条数据记录,都是可唯一标识的。而且所有 "非主键" 字段,都必须完全依赖 "主键",不能只依赖 "主键" 的一部分。当我们知道 "主键" 的值("联合主键" 的值)时,就可以检索到一行数据中的任何属性的任何值。
一句话表示:表中必须要有 "主键" 字段,而且所有的"非主键" 字段必须完全依赖 "主键" 字段。
举例1:
成绩表(学号,课程号,成绩),(成绩)必须通过 (学号 )和 (课程号)检索才能得到。所以,(成绩)依赖于(学号)和(课程号),这就是 完全依赖关系。
举例2:
比赛表(球员编号,姓名,年龄,比赛编号,比赛时间,比赛场地等..),这里候选键和主键都是(球员编号,比赛编号)。表中关系如下:
(球员编号,比赛编号) --> (姓名,年龄,比赛时间,比赛场地,得分)
但是该表不满足 第二范式(2NF),因为数据表中的字段还存在着其他的对应关系(不是完全依赖的):
(球员编号) --> (姓名,年龄) (比赛编号) --> (比赛时间,比赛场地) (球员编号,比赛编号) --> (得分)
该表中的"非主属性",是不完全依赖 "候选键"(主键),所以该表不符合 第二范式(2NF)
符合 第二范式(2NF)的做法:(根据不同的对应关系,分别建立相应的表)
表名 属性(字段) 球员表 球员编号,姓名和年龄。 比赛表 比赛编号,比赛时间和比赛场地。 得分表 得分编号,球员编号,比赛编号,得分。
不遵循第二范式(2NF) 设计的表,可能会产生的问题:
- 数据冗余:如果一个球员参加了 m 场比赛,那么球员信息就重复了 m-1 次。一个比赛可能会有 n 个球员参加,比赛信息就重复了 n-1 次。
- 插入异常:如果我们想要添加一次新的比赛,但是无法确定参加的人员,那么就无法插入。
- 删除异常:如果要删除球员,就会同时把比赛信息删除掉。
- 更新异常:如果我们调整了某个比赛的时间,那么表中所有这个比赛的时间都需要调整,否则就会出现数据不一致的情况。
个人理解:
数据耦合度高,重复数据多(在实际开发中如果这样设计表,也会在开发时带来诸多问题)。
- 小结:
第二范式(2NF)要求 表的字段完全依赖 "超键"(主键)。如果存在不完全依赖,那么这些字段和 "候选键"(主键)应该分离出来形成一个新的表,新表和原表之间是一对多的关系。
第一范式(1NF) 告诉我们字段属性需要是"原子性"的,而 第二范式(2NF) 告诉我们一张表就是一个独立的对象,一张表只表达一个意思。
2.6 第三范式(3NF)
第三范式(3NF)在 第二范式(2NF) 的基础上,确保数据表中的每一个 "非主键" 字段都和 "主键" 字段的关系。换句话就说是:数据表中的所有 "非主键 "字段不能依赖于其他 "非主键" 字段(所有的 "非主键" 字段之间不能有依赖关系,必须 "相互独立")。
举例1:
- 部门信息表:每个部门有 - 部门编号,部门名称,部门简介等信息。
- 员工信息表:每个员工有 - 员工编号,姓名,部门编号。当表中有了部门编号后就不能再将与部门有关的信息加入到员工表中了。
注意:如果不存在部门信息表,则根据第三范式(3NF)也应该构建它。否者有大量的数据冗余。
举例2:
字段名称 字段类型 主键 说明 id int Y 商品编号(主键) category_id int N 商品类别 id category_name varchar(30) N 商品类别名称 goods_name varchar(30) N 商品名称 proce decimal(10,2) N 商品价格 注意:商品类别名称(category_name) 依赖于 商品类别编号(category_id),不符合第三范式(3NF)
符合 第三范式(3NF) 的做法:(将有依赖关系的"非主键"字段拆分成表)
- 符合第三范式(3NF)的 商品类型表:
字段名称 字段类型 主键 说明 id int Y 商品类别主键id category_name varchar(30) N 商品类别名称
- 符合第三范式(3NF)的 商品表:
字段名称 字段类型 主键 说明 id int Y 商品主键id category_id varchar(30) N 商品类别id goods_name varchar(30) N 商品名称 price decimal(10,2) N 商品价格 这时如果要查询商品的类别名称,可以使用 商品表 的 商品类别id 与 商品类别表 的 主键id 进行关联。
小结:
第二范式(3NF)和第三范式(3NF)通常以这句话概括:每个 "非主键" 字段依赖于 "主键",依赖于完整的 "主键"(联合主键),并且相互之间不存在如何依赖关系。
2.7 小结
- 第一范式(1NF),确保每列保持 原子性
数据库的每一列都是不可分割的原子数据项,不可再分的最小数据单元,而不能是集合,数组,记录等非原子数据项。
- 第二范式(2NF),确保每列都和 "主键" 完全依赖
尤其在 "联合主键" 的情况下,"非主键" 字段不应该依赖于部分 "主键"。
- 第三范式(3NF),确保每列都和主键列 直接相关,并且 列于列之间不能有依赖关系,主键列除外。
范式的优缺点:
优点:数据的标准化有助于消除数据库中的 数据冗余,第三范式(3NF) 通常被认为在性能,扩展性和数据完整性方面 达到了最好的平衡。
缺点:范式的使用,可能 降低查询的效率。因为范式等级越高,设计出来的数据表越多,越精细,数据的冗余度就越低,进行数据查询的时候就可能需要 关联多张表,这不但代价昂贵,也可能使一些 索引策略无效。
结论:
范式只是提出了设计的标准,实际上设计数据表时,未必一定要符合这些标准。开发中,我们会出现为了性能和读取效率而违反范式化的原则,通过 增加少量的冗余 或重复的数据来提高数据库的 读性能,减少关联查询,join 表的次数,实现 空间换取时间 的目的。因此在实际的设计过程中要理论结合实际,灵活运用。
范式本身没有优劣之分,只有适用的场景不同。没有完美的设计,只有合适的设计,我们在数据表的设计中,还需要 根据需求将 "范式" 和 "反范式化" 混合使用。
3. 反范式化
3.1 概述
有的时候不能简单按照规范要求设计数据表,因为有的数据看似冗余,其实对业务来说十分重要。这个时候,我们就要遵循 业务优先 的原则,首先满足业务需求,再尽量减少冗余。
当数据库中的数据量比较大时,如果完全按照 MySQL 的三大范式设计数据表,读数据时会产生大量的关联查询,在一定程度上会影响数据库的读性能。如果我们想对查询效率进行优化,反范式优化 也是一种优化思路。此时,可以通过在数据表中 增加冗余字段 来提高数据库的读性能。
规范化 vs 性能
- 为满足某种利益,数据库性能比规范化数据库更重要
- 在数据库规范化的同时,要综合考虑数据库的性能
- 通过在给定的表中添加额外的字段(增加冗余字段),以大量减少需要从中搜索信息所需的时间
- 通过在给定的表中插入 "计算列",以方便查询
3.2 应用举例
举例1:
员工的信息存储在 员工表 中,部门信息存储在 部门表 中。通过 员工表 中的 部门id字段 与 部门表 建立关联关系。如果要查询员工所在部门的名称就必须进行关联查询:
SELECT
员工名称,
部门名称,
部门id
FROM
员工表
INNER JOIN 部门表 ON 员工表.部门id字段 = 部门表.id
如果经常需要查询员工所在部门的名称,连接查询就会浪费很多时间。我就可以在 员工表 中增加一个冗余字段 部门名称,这样就不用每次都进行连接查询了。
举例2:
反范式化 的 商品信息表 设计如下:
字段名称 | 字段类型 | 主键 | 说明 |
---|---|---|---|
id | int | Y | 商品编号(主键) |
category_id | int | N | 商品类别 id |
category_name(冗余字段) | varchar(30) | N | 商品类别名称(冗余字段) |
goods_name | varchar(30) | N | 商品名称 |
proce | decimal(10,2) | N | 商品价格 |
通过增加 category_name 冗余字段,提高查询的效率。
举例3:(按照自己的理解来!)
通过在数据库分别按照 "三大范式" 和 "反范式化" 设计表,然后进行性能对比(要在数据量和结果相同的情况下)!!!
3.3 反范式化的问题
反范式化可以 通过空间换时间,提升查询的效率,但是反范式化也会带来一些问题:
- 存储数据的 空间变大 了
- 一个表中字段做了修改,另一个表中冗余的字段也需要做同步修改,否则 数据不一致
- 若采用 "存储过程" 或 "触发器" 来支持数据的更新,删除等额外操作,如果更新频繁,会非常 消耗系统资源 或 产生行锁
- 在 数据量小 的情况下,反范式不能体现性能的优势,可能还会让数据库的设计更加 复杂
3.4 反范式化的适应场景
当冗余信息有价值或能 大幅度提高查询效率 的时候,我们才会采取反范式化的优化。
3.4.1 增加冗余字段的建议
增加冗余字段一定要符合如下两个条件。只有满足这两个条件,才可以考虑增加冗余字段。
- 这个冗余字段 不会经常进行修改。
- 这个冗余字段 查询的时候经常被使用。
3.4.2 历史快照,历史数据的需要
在现实生活中,我们经常需要一些冗余信息,比如订单中的收货信息。每次发生的 订单收货信息 都属于 历史快照,需要进行保存,但用户可以随时修改自己的信息,这时保存这些冗余信息是非常有必须要的。
反范式化也常用在 数据仓库 的设计中,因为数据仓库通常 存储历史数据,对增删改的实时性要求不强,对历史数据的分许需求强。这时使用允许数据的冗余度,更方便进行数据分析。
数据仓库 和 数据库 在使用上的区别:
- 数据库 设计的目的在于 捕获数据,而 数据仓库 设计的目的在于 分析数据
- 数据库 对数据的 增删改实时性 要求强,需要存储在线的用户数据,而 数据仓库存储的一般都是 历史数据
- 数据库 设计需要 尽量避免冗余,但为了提高查询效率也 允许一定的冗余度 ,而 数据仓库在设计上更偏向采用反范式设计。
4. 巴斯范式(BCNF)
巴斯范式(BCNF)是在 第三方式(3NF)的基础上进行了改进,不是什么新的范式,只是对 第三范式(3NF)中设计规范要求的加强,使得数据的冗余度更小。所以,称为是 修正的第三范式,或 扩充的第三范式。(所有的 "主属性" 字段之间不能有依赖关系)
若一个表的设计达到了第三范式,并且它只有一个候选键,或者它的每个候选键都是单属性(不是联合主键),则直接达到巴斯范式(BCNF)。
1.案例:物品表
仓库名 管理员 物品名 数量 河南仓 张三 烩面 10 河南仓 张三 蒸面 5 浙江仓 李四 东坡肉 100 上海仓 王五 小赤佬 0 在该表中,一个仓库只有一个管理员,同时一个管理员也只管理一个仓库。仓库名决定了管理员,管理员也决定了仓库名,(仓库名,物品名)这两个字段可以唯一标识(数量)这个字段。
- 候选键:(管理员,物品名)和(仓库名,物品名),然后从中选一个作为 "主键" (仓库名,物品名)
- 主属性:"候选键" 中的任何一个属性(字段),也就是(仓库名,管理员,物品名)
- 非主属性:数量
2.判断是否符合第三范式(3NF):
如何判断一张表的范式呢?我们需要根据范式的等级,从低到高来进行判断。
- 首先先看是否符合 第一范式(1NF),该表的中每个属性(字段)都是 原子性 的,符合。
- 在看是否符合 第二范式(2NF),该表中的 "非主属性"(数量)都完全依赖于 "候选键"(主键),(仓库名,物品名)决定(数量),(管理员,物品名)决定(数量)。符合。
- 最后看是否符合 第三范式(3NF),该表中的 "非主属性"(数量)之间没有依赖关系 。符合。
3.该表目前存在的问题:
- 若增加一个仓库,但是还没有存放任何物品。根据数据表实体完整性的要求 主键不能有空值,因此会出现 插入异常。
- 如果仓库跟换了管理员,我们就可能会 修改数据表中的多条记录 。
- 如果仓库里的商品都卖空了,那么此时仓库名称和相应的管理员名称也会随之被删除。
可以看到,即便数据表遵循了 第三范式(3NF)的要求,同样可能存在插入,更新和删除数据的异常情况。
4.问题解决
要先分析一下造成异常的原因:"主属性"(仓库名)对于"候选键"(管理员,物品名)是部分依赖的关系,这样就有可能导致上面的异常情况。因此引入 巴斯范式(BCNF),它在第三范式(3NF)的基础上消除了"主属性"对"候选键"的部分依赖或者传递依赖关系。
- 如果在关系R中,U为主键,A属性是主键的一个属性,若存在A->Y,Y为主属性,则该关系不符合 巴斯范式(BCNF)
根据 巴斯范式(BCNF)的要求,需要吧仓库商品管理表拆分成下面这样:
- 仓库表:(仓库名,管理员)
- 库存表:(仓库名,物品名,数量)
这样就不存在 "主属性" 对于 "候选键" 的部分依赖或传递依赖,以上数据表的设计就符合 巴斯范式(BCNF)
- 个人练习(请你自己根据实际情况举例说说自己的理解!!!)
一个班级对应一个班主任,一个班主任对应一个班级。
有一张班级学生表:(班主任,班级,学生id)
这时该表就不符合 "巴斯范式(BCNF)"。正确的做法为:
- 班级表:(班级id,班主任)
- 班级学生表:(班级id,学生id)
5. 第四范式(4NF)
多值依赖的概念:
- 多值依赖 即 属性(字段)之间的一对多关系,记为 K->->A。
- 函数依赖 事实上是单值依赖,所以不能表达属性值(字段)之间的一对多关系。
- 平凡的多值依赖:全集 u=k+a,一个k可以对应于多个a,即 k->->a。此时 整个表就只有这一组一对多关系。
- 非平凡的多值依赖:全集 u=k+a+b,一个k可以对应于多个a,也可以对应于多个b,a与b相互独立,即 k->->a,k->->b。整个表有多组一对多关系,且有:一部分是相同的属性集合,多部分是相互独立的属性集合。
第四范式(4NF)即在满足 巴斯-科德范式(BCNF)的基础上,消除 非平凡和非函数依赖的多值依赖,保留平凡的多值依赖(把同一表内的多组的 "一对多" 关系删除,尽量只保留一组)。
举例1:职工表(职工编号,职工孩子,职工选修课程)
在这个表中,同一个职工可能会有多个孩子。同样,同一个职工也可能会有多个选修课程,即 该表中存在着多个多值依赖,不符合第四范式(4NF)。
如果要符合 第四范式(4NF),只需要将该表分为两个表,使它们只有一个多值依赖。
- 职工孩子表:(职工编号,职工孩子)
- 职工选修表:(职工编号,职工选修课程)
这样,两个表都只有一个多值依赖了,所以符合第四范式(4NF)
举例2:
个人练习(请你自己根据实际情况举例说说自己的理解!!!)
.....
6. 第五范式(5NF),域键范式(DKNF)
除了第四范式外,我们还有更高级的第五范式(又称完美范式)和域键范式(DKNF)
在满足 第四范式(4NF)的基础上,消除了表关系模式中不是由 "候选键" 所蕴含的连接依赖。如果关系模式R中的每一个连接依赖均由R的 "候选键" 所隐含,则称此关系模式符合 第五范式(5NF)
"函数依赖" 是 "多值依赖" 的一种特殊的情况,而 "多值依赖" 实际上是 "连接依赖" 的一种特殊情况。但 "连接依赖" 不像 "函数依赖" 和 "多值依赖" 可以由 语义直接导出,而是在 关系连接运算 时才反映出来。存在 "连接依赖" 的关系模式还是有可能遇到数据冗余及插入,修改,删除异常等问题。
- 第五范式(5NF) 处理的是 无损连接问题,这个范式基本 没有任何实际意义。因为无损连接很少出现。
- 域键范式(DKNF) 试图定义一个 终极范式,该范式考虑所有的依赖和约束类型,但是实用价值是最小的,只存在理论研究中。
7 . 实战案例
wocao 自己想办法吧!!!
8.ER 模型
8.1 ER 模型概述
ER 模型也叫作 实体关系模型,是用来描述现实生活中客观存在的事物,事物的属性,以及事物之间关系的一种数据模型。在数据库设计阶段,通常使用 ER 模型来描述需求和信息特性,帮助我们梳理业务逻辑,从而设计出优秀的数据库。
- ER 模型是什么:ER 模型也叫作 实体关系模型。
- ER 模型能做什么:可以用来 描述事物,事物的属性,事物之间的关系。
- 为什么要使用 ER 模型:使用 ER 模型可以帮助我们 梳理业务逻辑,设计出优秀的数据库。
8.2 ER 模型包括的要素
ER 模型中有三个要素,分别是实体,属性和关系。
- 实体:可以看做是对象,往往对应于现实生活中的真实存在的个体。可分为两类,分别是 强实体 和 弱实体。强实体 是指 不依赖于其他实体的实体;弱实体 是指 于另一个实体有很强的依赖关系的实体。
- 属性:则是指实体的特性(对象中的属性)。
- 关系:则是指 各个实体之间的依赖关系。
注意:实体和属性不容易区分。这里提供一个原则:从系统整体的角度来看,可以独立存在的是实体,不可再分的是属性。
8.3 关系的类型
在 ER 模型中的关系有:一对一,一对多,多对多。
- 一对一:表示 实体之间是一一对应的。(一个人只能有一个身份证号)
- 一对多:表示 一个实体对应多个另外一边的实体。(一个班级对应多个学生,一个学生对应一个班级)
- 多对多:表示 两个实体都可以通过关系对应多个对方的实体。(一个超市有多种货物,一个货物多个超市都有)
8.4 自己想办法吧!
可以通过某个业务模块的,通过 ER 模型的方式进行数据库设计。
9. 数据表的设计原则
数据表设计的原则可总结为: "三少一多"。
9.1 表的个数越少越好
数据表越少,就说明实体和关系设计得越简洁,既方便理解又方便操作。
9.2 表中的字段个数越少越好
字段个数越多,数据冗余的可能性越大。设置字段个数少的前提是各个字段相互独立,而不是某个字段的取值可以由其他字段计算出来。当然字段个数少是相对的,我们通常会在 数据冗余 和 检索效率 中进行平衡。
9.3 表中联合主键的字段个数越少越好
设置主键是为了确定唯一性,当一个字段无法确定唯一性的时候,就需要采用联合主键的方式(也就是用多个字段来定义一个主键)。联合主键中的字段越多,占用的索引空间越大,不仅会加大理解的难度,还会增加运行时间和索引空间,因此联合主键的字段个数越少越好。
9.4 使用主键和外键越多越好
数据库的设计实际上就是定义各种表,以及各种字段之间的关系。这些关系越多,就说明这些实体之间的 冗余度越低,利用度越高。这样不仅保证了表之间的 独立性,还能提升相互之间的关联使用率。
"三少一多" 原则的核心就是 简单可复用。
简单指的是 用更少的表,更少的字段,更少的联合主键字段 来完成数据表的设计。
可复用则是 通过主键,外键的使用来增强数据表之间的复用率。因为一个主键可以理解是一张表的代表。主键设计的越多,证明它们之间的利用率越高。
注意:以上原则并不是绝对的,有时候我们也需要牺牲数据的冗余度来换取数据处理的效率。
10. 数据库对象编写建议
以下的数据库使用规范适用于大多数公司,这样使用数据库可以发挥出更高的性能。
10.1 关于库
- 【强制】库的名称 必须控制在 32 个字符以内,只能使用英文字母,数字和下划线,建议以 英文开头。
- 【强制】库名中英文 一律小写,不同的单词采用 下划线 分割。须见名知意。
- 【强制】库的名称格式:业务系统名称_子系统名。
- 【强制】库名 禁止使用关键字。
- 【强制】创建数据库必须 显示指定字符集,并且字符集只能是utf8或者utf8md4。
create database xld_data_base default character set 'utf8';
-
【建议】对于程序连接数据库账号,遵循 权限最小原则
-
【建议】临时库以 tmp_ 为前缀,并以日期为后缀;
备份库以 bak_ 为前缀,并以日期为后缀;
10.2 关于表,列
-
【强制】表和列的名称 必须控制在 32 个字符以内,只能使用英文字母,数字和下划线,建议以 英文开头。
-
【强制】表名,列名一律小写,不同的单词采用 下划线 分割。须见名知意。
-
【强制】表名要求有模块名强相关,同一模块的表名尽量使用 统一前缀。比如:xld_user
-
【强制】创建表时必须 显示指定字符集 为utf8或utf8md4。
-
【强制】表名,列名 禁止使用关键字。
-
【强制】创建表时必须 显示指定表存储引擎 类型。如无特殊需求,一律为 InnoDB。
-
【强制】建表必须有 comment(备注)。
-
【强制】字段命名应尽可能使用表达实际含义的 英文单词 或 缩写 。如:公司id,不要使用 corporation_id,而是 corp_id 即可。
-
【强制】布尔值类型的字段命名为 is_描述。如:is_normal(是否正常)
-
【强制】禁止在数据库中存储图片,文件等大的二进制数据。
-
【建议】创建表时 必须要有主键。
强制要求主键为id,类型为 int 或 bight,且为 auto_increment 建议使用 unsigned 无符号型。
-
【建议】核心表必须有行数据的 创建时间字段(create_time)和 最后更新时间字段(update_time)。
-
【建议】表中所有字段尽量都是 not null (非空) 的,可以根据业务需要定义 default(默认值)。
-
【建议】所有存储相同数据的 列名和列类型必须一致(特别是关联列,必须一致,否则为导致索引失效)。
-
【建议】临时表以 tmp_ 为前缀,并以日期为后缀;
备份表以 bak_ 为前缀,并以日期为后缀;
10.3 关于索引
- 【强制】InnoDB 表主键必须为 id int / bigint auto_increment。且主键值 禁止更新。
- 【强制】InnoDB 和 MyISAM 存储引擎表,索引类型必须为 BTREE。
- 【建议】主键名称以 pk 开头,唯一索引 以 uni 或 uk 开头,普通索引以 idx 开头,一律使用小写格式,以字段的名称或缩写作为后缀。
- 【建议】单个表上的索引个数 不能超过6个。
- 【建议】在建立索引时,多考虑建立 联合索引,并把区分度最高的字段放在最前面。
- 【建议】在多表 join 的 sql 里,尽量保证 "被驱动表" 的关联字段上有索引。
- 【建议】建表或加索引时,保证表里不存在 冗余索引。
10.4 sql 编写
- *【强制】查询时 select 语句必须指定具体字段名称,禁止使用 。
- 【建议】插入时 insert 语句指定具体字段名称,禁止 insert into 表名 values (....) 写法。
- 【建议】除静态表或小表(100行内),DML语句 必须有 where 条件,且使用索引查找。
- 【建议】插入时 insert into ... values(xx),(xx),(xx),... 这里xx的不要超过5000个。会引起 主从同步延迟。
- 【建议】查询时 select 语句不要使用 union,推荐使用 union all,且 union all 字句要限制在 5 个以内。
- 【建议】关联查询 join 时不要超过 5 个表。
- 【建议】减少使用 order by,建议在程序端排序。
- 【建议】包含了 order by,group by,distinct 这些查询的语句,where 过滤后结果请保持在 1000 行以内,否则 SQL 会很慢。
- 【建议】对单表的 多次 alter 操作必须合并为一次。(alter table 时会产生 表锁)
- 【建议】批量操作数据时,需要控制事务处理间隔时间,进行必要的 sleep(睡眠)。
- 【建议】事务里包含 SQL 不要超过 5 个。(过长的事务会导致锁时间较久,连接消耗过多等问题)
- 【建议】事务里更新语句尽量基于主键或者 unique key,如:update 表名 set ... where ...。否则会产生间隙锁,内部扩大锁定范围,导致系统性能下降,产生 死锁。
11. E-R 模型的工具使用 - PowerDesigner
天呐!!!
这个就需要自己想办法了。百度吧!!!