一、数据库设计的重要性:
1.糟糕的数据库设计产生的问题:
(1)数据冗余、信息重复、存储空间浪费
(2)数据更新、插入、删除的异常
(3)无法正确表示信息
(4)丢失有效信息
(5)程序性能差
2.良好的数据库设计有以下优点:
(1)节省数据的存储空间
(2)能够保证数据的完整性
(3)方便进行数据库应用系统的开发
二、范式
1.范式简介:
在关系型数据库中,关于数据表设计的基本原则、规则称为范式,可以理解为一张数据表的设计结构需要满足的某种设计标准的级别。范式时关系型数据库理论的基础,时在设计数据库结构中所要遵循的规则和指导方法。
2.范式的种类:
按照范式级别,范式从低到高分别是:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯—科德范式(BCNF),第四范式(4NF)和第五范式(5NF)。数据库的范式设计越高阶,冗余度就越低,同时高阶的范式一定符合低阶范式的要求,满足最低要求的范式是第一范式,在第一范式的基础上进一步满足更多规范要求的称为第二范式,其余范式以此类推。一般来说在关系型数据库的设计中最高要达到BCNF,普遍还是3NF。有时为了提高查询性能还要破坏范式化。
3.键和相关属性概念:
(1)超键:能够唯一标识元组的属性集称为超键
(2)候选键:如果超键不包括多余的属性,那么这个超键就是候选键
(3)主键:从候选键中选择一个作为主键
(4)外键:如果数据表R1中的某属性集不是R1的主键,而是另一个数据表R2的主键,那么这个属性集就是数据表R1的外键
(5)主属性:包含在任一候选键中的属性
(6)非主属性:不包含在任一候选键中的属性。
4.第一范式(1st NF):第一范式主要是确保数据表中的每个字段的值必须具有原子性,也就是说数据表中的每个字段的值为不可拆分的最小单元。
5.第二范式(2NF):在满足第一范式的基础上,还要满足数据表中的每一条记录都是唯一可以标识的,而且所有的非主键字段都必须完全依赖于主键,不能只依赖主键的一部分。也就是如果知道主键的所有属性的值,就可以检索到任何元组的任何属性的任何值。2NF要求实体的属性集完全依赖于主关键字,如果不存在完全依赖,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体和原实体之间是一对多的关系。
6.第三范式(3NF):在满足第二范式的基础上确保数据表中的每一个非主键字段都和主键字段直接相关,也就是说要求数据表中的所有非主键字段不能依赖于其他非主键字段。也就是说所有非主键属性之间必须相互独立。
第一范式确保每列的原子性,数据库的每一项都是不可分割的原子数据项,不可再分的最小数据单元,而不能是集合、数组、记录等非原子数据项。第二范式确保每列都和主键完全依赖,尤其在复合主键的情况下,非主键部分不应该依赖于部分主键。第三范式确保每列都和主键列直接相关,而不是间接相关。
7.巴斯范式(BCNF):BCNF被称为是修正的第三范式或扩充的第三范式,若一个第三范式只有一个候选键并且它的每个候选键都是单属性,那么该关系自然到达BCNF。
8.第四范式:第四范式是在BCNF的基础上消除非平凡且非函数依赖的多值依赖(即把同一表内的多对多关系删除)
多值依赖:属性之间的一对多关系,记为k→→A。
函数依赖:实际上是单值依赖,不能表达属性值之间的一对多关系
平凡的多值依赖:全集U=K+A,一个K可以对应多个A,即k→→A,此时整个表就是一组一对多关系
非平凡的多值依赖:全集U=K+A+B,一个K可以对应于多个A,也可以对应于多个B,A与B互相独立
范式的优点:数据的标准化有助于消除数据库中的数据冗余,3NF被认为在性能、扩展性和数据完整性方面达到了最好的平衡。
范式的缺点:范式可能降低查询的效率,因为范式等级越高,设计出的数据表越多、越精细、数据冗余度就越低,进行数据查询的时候就可能需要关联多张表,也可能导致索引失效。
三、反范式化:
1.概述:
有的时候不能简单按照规范要求设计数据表,因为有的数据看似冗余,其实对业务来说十分重要。这个时候,就要遵循业务优先的原则,首先满足业务需求,再尽量减少冗余。如果数据库中的数据量比较大,系统的UV和PV访问频次比较高,则完全按照MySQL的三大范式设计数据表,读数据时会产生大量的关联查询,在一定程度上会影响数据库的读性能。如果想对查询效率进行优化,反范式优化也是一种优化思路。此时可以通过在数据表中增加冗余字段来提高数据库的读性能。
2.反范式化带来的问题:
(1)存储空间变大
(2)一个表中的字段做了修改,另一个表中的冗余字段也需要同步修改,否则数据不一致
(3)若采用存储过程来支持数据的更新、删除等额外操作,如果频繁更新会非常消耗系统资源
(4)在数据量小的情况下反范式化可能会让数据库设计更加复杂
3.反范式化应用场景:
(1)增加冗余字段的建议:这个冗余字段不需要经常修改或者查询时不可或缺
(2)历史快照、历史数据的需要
四、ER模型:
ER模型叫做实体关系模型,是用来描述现实生活中客观存在的事物、事物的属性、以及事物之间关系的一种数据模型,在开发基于数据库的信息系统设计阶段,通常使用ER模型来描述信息需求和信息特性。
1.ER模型包括的要素:
ER模型有三个要素,分别是实体、属性和关系。实体可以看作是数据对象,往往对应与现实生活中的真实存在的个体。在ER模型中使用矩形表示。实体分为强实体和弱实体,强实体是指不依赖于其他实体的实体,弱实体是指对另一个实体有很强的依赖关系的实体。属性是指实体的特性,在ER模型中使用椭圆形表示,关系是指实体之间的联系,在ER模型中使用菱形表示。可以存在的是实体,不可再分的是属性。
2.关系的类型:
(1)一对一:实体之间的关系是一一对应的;
(2)一对多:一边的实体通过关系可以对应于多个另外一边的实体;
(3)多对多:两边的实体都可以通过关系对应多个对方的实体。
3.ER模型转换成数据表:
(1)一个实体通常转换成一个数据表
(2)一个多对多的关系通常转换成一个数据表
(3)一个1对1或者一对多的关系通过表的外键表达,而不是设计一个新的数据表
(4)将属性转换为表的字段
五、数据表的设计原则(三少一多):
1.数据表的个数越少越好:RDBMS 的核心在于对实体和联系的定义,也就是E-R图,数据表越少,证明实体和联系设计得越简洁,既方便理解又方便操作。
2.数据表中的字段个数越少越好:字段个数越多数据冗余的可能性越大。设置字段个数少的前提是各个字段相互独立,而不是某个字段的取值可以由其他字段计算出来。当然字段个数少是相对的,通常会在数据冗余和检索效率中进行平衡。
3.数据表中联合主键的字段个数越少越好:设置主键是为了确定唯一性,当一个字段无法确定唯一性的时候,就需要采用联合主键的方式(也就是用多个字段来定义一个主键)。联合主键中的字段越多,占用的索引空间越大,不仅会加大理解难度,还会增加运行时间和索引空间,因此联合主键的字段个数越少越好。
4.使用主键和外键越多越好:数据库的设计实际上就是定义各种表,以及各种字段之间的关系。这些关系越多,证明这些实体之间的冗余度越低,利用度越高。这样做的好处在于不仅保证了数据表之间的 独立性,还能提升相互之间的关联使用率。
六、数据库对象编写建议
1.关于数据库:
(1)强制:库的名称必须控制在32个字符以内,只能使用英文字母、数字和下划线,建议以英文字母开头。
(2)强制:库名中英文一律小写 ,不同单词采用下划线分割。须见名知意。
(3)强制:库的名称格式:业务系统名称_子系统名。
(4)强制:库名禁止使用关键字(如type,order等)
(5)强制:创建数据库时必须显式指定字符集,并且字符集只能是utf8或者utf8mb4
(6)建议:对于程序连接数据库账号,遵循权限最小原则,使用数据库账号只能在一个DB下使用,不准跨库。程序使用的账号原则上不准有drop权限
(7)建议:临时库以 tmp_为前缀,并以日期为后缀;备份库以 bak_为前缀,并以日期为后缀。
2.关于表和列:
(1)强制:表和列的名称必须控制在32个字符以内,表名只能使用英文字母、数字和下划线,建议以 英文字母开头。
(2)强制:表名、列名一律小写,不同单词采用下划线分割。须见名知意
(3)强制:表名要求有模块名强相关,同一模块的表名尽量使用统一前缀。
(4)强制:表名、列名禁止使用关键字(如type,order等)
(5)强制:创建表时必须 显式指定字符集为utf8或utf8mb4
(6)强制:创建表时必须显式指定表存储引擎 类型。如无特殊需求,一律为InnoDB
(7)强制:建表必须有comment
(8)强制:字段命名应尽可能使用表达实际含义的英文单词或缩写。
(9)强制:布尔值类型的字段命名为 is_描述。
(10)强制:禁止在数据库中存储图片、文件等大的二进制数据,通常文件很大,短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机IO,操作文件很大时,IO操作很耗时。通常存储于文件服务器,数据库只存储文件地址信息。
(11)建议:建表时表必须有主键,强制要求主键为id,类型为int或bigint,且为auto_increment。建议使用unsigned无符号型。标识表里每一行主体的字段不要设为主键,建议设为其他字段并建立unique key索引。因为如果设为主键且主键值为随机插入,则会导致innodb内部页分裂和大量随机I/O,性能下降
(12)建议:核心表(如用户表)必须有行数据的创建时间字段和最后更新时间字段
(13)建议:表中所有字段尽量都是NOT NULL属性,业务可以根据需要定义DEFAULT值。因为使用NULL值会存在每一行都会占用额外存储空间、数据迁移容易出错、聚合函数计算结果偏差等问题。
(14)建议:所有存储相同数据的列名和列类型必须一致
(15)建议:中间表(或临时表)用于保留中间结果集,名称以 tmp_开头。备份表用于备份或抓取源表快照,名称以bak_开头。中间表和备份表定期清理。
3.关于索引:
(1)强制:InnoDB表必须主键为id int/bigint auto_increment,且主键值禁止被更新。
(2)强制:InnoDB和MyISAM存储引擎表,索引类型必须为BTREE
(3)建议:主键的名称以 pk_开头,唯一键以 uni_或 uk_开头,普通索引以 idx_开头,一律使用小写格式,以字段的名称或缩写作为后缀。
(4)建议:多单词组成的columnname,取前几个单词首字母加末单词组成column_name
(5)建议:单个表上的索引个数不能超过6个
(6)建议:在建立索引时,多考虑建立联合索引并把区分度最高的字段放在最前面。
(7)建议:在多表JOIN的SQL里保证被驱动表的连接列上有索引,这样JOIN 执行效率最高
(8)建议:建表或加索引时,保证表里互相不存在余索引。
4.关于SQL编写:
(1)强制:程序端SELECT语句必须指定具体字段名称,禁止写成*。
(2)建议:程序端insert语句指定具体字段名称,不要写成INSERTINTO t1 VALUES(...)
(3)建议:除静态表或小表(100行以内),DML语句必须有WHERE条件,且使用索引查找。
(4)建议:INSERT INTO..VALUES(XX),(XX),(XX)..这里XX的值不要超过5000个。值过多虽然上线很快,但会引起主从同步延迟。
(5)建议:SELECT语句不要使用UNION,推荐使用UNION ALL,并且UNION子句个数限制在5个以内。
(6)建议:线上环境,多表JOIN不要超过5个表。
(7)建议:减少使用ORDER BY,能不排序就不排序,或将排序放到程序端去做。ORDER BY、GROUP BY、DISTINCT这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。
(8)建议:包含了ORDER BY、GROUP BY、DISTINCT这些查询的语句,WHERE 条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢。
(9)建议:对单表的多次alter操作必须合并为一次