数据库基础
1 范式化设计
1.1 第一范式(1NF)无重复的列
定义
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。简而言之,第一范式就是无重复的列。
上面这张表就不符合第一范式,有重复的列,应该将 name-age 拆分成 name 和 age 两个列
1.2 第二范式(2NF)属性完全依赖于主键
定义
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。
上面这张订单表,我们看到订单 id 为 1 的订单包含了两个产品,没有一个唯一的列被称为关键字,所以需要加上一个关键字主键
这张表需要优化成
订单表主表:
订单表明细
这样订单明细表中就依赖关键字 id,id 是唯一的主键,符合第二范式
1.3 第三范式(3NF)属性不依赖于其它非主属性
满足第三范式(3NF)必须先满足第二范式(2NF)。第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
上面这个订单表的设计,它依赖的商品信息过多,应该将商品信息拆分出来一张表单独存放
商品表中存储商品相关信息,订单表中只存放商品 id,不存储名称等信息
总结
在日常工作中,我们在数据库设计方面基本上满足以上三个范式就可以了,至于BCNF范式
,第四范式(4NF)
作为了解即可
2 数据库常用类型
设计表时类型选择应该遵循以下几个规则
1、更小的字段,CPU 处理周期更少
2、简单就好
3、尽量避免 NULL
2.1 整数类型
类型 | 字节数 | 说明 |
---|---|---|
tinyint | 1 字节 | 对应 Java 中的 boolean 类型 |
smallint | 2 字节 | |
mediumint | 3 字节 | |
int | 4 字节 | |
bigint | 8 字节 | 对应 Java 中的 Long 类型 |
无符号
在数据库管理工具中我们进程见到无符号这个选项
勾选了无符号,说明这个字段只能存储正数,有符号则存储范围是负数~正数
2.2 实数类型
MySQL 实数有三种类型,float
、double
、decimal
类型 | 说明 | 字节数 |
---|---|---|
float | 单精度浮点数 | 4 个字节 |
double | 双精度浮点数 | 8 个字节 |
decimal(M, D) | 压缩的“严格”定点数 | M + 2 个字节 |
实数类型通常用于存储有小数的场景下,比如金额,重量等
博主在使用 MySQL 中这几个类型使用较少,这里给大家提供一些金额类字段存储的设计思路
我们存储金额一般会精确到分,比如 12.54 元,那么在存储的时候我们可以用整型,将 12.54 * 100 存储数据库中,取出的时候再进行相应的转化。有的同学可能会有疑问,这样做不是很麻烦。为什么这么做,这里就涉及到计算的精度问题了,如果计算时发现需要四舍五入,那么就可以考虑将数值乘以相应的倍数,100、1000 倍
2.3 字符串类型
类型 | 长度范围 | 字节数 |
---|---|---|
char(M) | 0 <= M <=255 | M 个字节 |
varchar(M) | 0 <= M <= 65535 | M + 1 个字节 |
enum | 1 <= M <= 65535 | 1或者2 个字节 |
tinytext | 0 <= M <=255 | M + 2 个字节 |
text | 0 <= M <= 65535 | M + 2 个字节 |
mediumtext | 0 <= M <= 16777215 | M + 3 个字节 |
longtext | 0 <= M <= 4294967295 | M + 4 个字节 |
工作中常用的就是 char、varchar 和 text 类型
char
- char(M) 类型一般需要预先定义字符串长度。如果不指定(M),则表示长度默认是
1
个字符。 - char 是定长字符串,保存数据时,数据的实际长度比 char 类型声明的长度小,则会在右侧填充空格以达到指定的长度。当MySQL检索 char 类型的数据时,char 类型的字段会去除尾部的空格。
- 定义 char 类型字段时,声明的字段长度即为 char 类型字段所占的存储空间的字节数。
varchar
- varchar 使用时,必须指定长度,不指定则会报错
- varchar 因为是变长字符串,需要额外地在长度列表上存放实际的字符长度:小于255为1个字节,大于255则要2个字节
- varchar 是变长字符串,比如说 varchar(10),你存储了 "ok",那么 varchar 实际存储就是两个字节,而 char(10) 则是存储了 10 个字节
- 在不同的字符集下,varchar 的大小是不一样的,很多人可能会试过,varchar 最大可用存储 65535 个字节,为什么我设置 65535 会报错,这是因为我们存储通常用的是 utf8 字符集,在 utf8 字符集下,每个字符占用 3 个字节,所以在 utf8 字符集下 varchar(N) 中的N 最大为 (65535-1-2)/3
char 和 varchar 如何选择
1、存储很短的信息。比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个byte用于存储信息长度,本来打算节约存储的,结果得不偿失。
2、固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据长度的特性就消失了,而且还要占个长度信息。
3、十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。
扩展:
4.0版本以下,varchar(20),指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字节)
5.0版本以上,varchar(20),指的是20字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放20个,最大大小是65532字节
gbk:每个字符最多占用2个字节
utf8:每个字符最多占用3个字节
text
text 通常用于存储大文本,比如文章,评论等
3 设计规范
以下设计规范出自阿里巴巴开发手册
- 【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint
(1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在
从 is_xxx 到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的
命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
- 【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只
出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、
表名、字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
- 【强制】表名不使用复数名词。
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数
形式,符合表达习惯。
-
【强制】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
-
【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
- 【强制】小数类型为 decimal,禁止使用 float 和 double。
说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不
正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
-
【强制】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
-
【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长
度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索
引效率。
- 【强制】表必备三字段:id, gmt_create, gmt_modified。
说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。gmt_create,
gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被
动更新。
- 【推荐】表的命名最好是加上“业务名称_表的作用”。
正例:alipay_task / force_project / trade_config
-
【推荐】库名与应用名称尽量一致。
-
【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
-
【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1)不是频繁修改的字段。
2)不是 varchar 超长字段,更不能是 text 字段。
正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存
储类目名称,避免关联查询。
- 【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
- 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检
索速度。
4 常用 SQL 语句
常用 SQL 语句可以看我之前写的一篇博客,里面详细记录了常用的查询语句