MySQL建表实战总结-从建模到表设计&数据类型选择

132 阅读6分钟

MySQL建表实战总结

1.根据业务建模

这个过程中主要是梳理业务实体之间的关系,画出相应的实体图。

关系主要有三种:一对一,一对多,多对多。

实体映射成表的经验之谈

建表根据关系行为也会有所不同。

  • 如果是一对一的关系,如A与B是一对一的关系,B又与其他实体没有其他关系,那么B其实完全可以作为A表的一部分。

  • 如果是多对多的关系,那么往往需要建立一张中间表。

  • 如果是一对多的关系,如A与B是一对多的关系,那么B表大概需要维护A表id的映射


create table B(

Aid bigint,

...其他字段

);

一对多到了代码编写环节可能会反过来变成这样:


public class A(

List<B> list;

);

区分:业务主键,数据库主键

  • 数据库主键:id bigint,加主键约束。

无论如何,记住,数据库主键是一定要存在的

  • 业务主键是否存在看具体业务。

如办公室编号这些属于业务主键,它往往可能有唯一性,但是不会加主键索引,往往也不会加唯一性索引(考虑is_delete的逻辑删除,而且也不会作为联表条件)。

业务主键不加主键索引主要在于考虑删除。

  • 加主键索引的字段一般是不能改的,如果改的话会发生什么?

主键索引字段的修改实际上等于 插入+删除 的操作。

2.选择合适的数据类型

(1)字符类型

误区一: char(M) 固定占M*w个字节。(w是选择的字符集规定的每个字符最大的长度)

实际上,取决于 MySQL选用的行格式与字符集,如compact行格式选择的字符集latin1或者utf8mb4

  • 在compact行格式下,选择的字符集如果是可变字符集utf8mb4

(可变字符集指的是:每一个字符存储占用的字节是可变的。如utf8mb4中,汉字一般三个字节,邮箱表情一般四个字节)

  • 在redundant行格式下,char(M)占用的字节确实等于M*字符集规定的最大长度

误区二:varchar(M)实际占用的内存大小
  • varchar(M)实际占用的字节=L字符存储实际占用的字节+长度

  • varchar(M)的可变不止是存储的字符数量的可变,甚至还是存储数量长度的可变。

  • 字符数量的可变性:

不会浪费未达到M个字符的空间,用多少就分配多少空间。

  • 长度的可变性:可能占用1个字节或者2个字节。

1个字节:字符存储占用0-255bytes

2个字节:字符存储占用256-65535bytes

  • 实际上,text与varchar的区分只是text不具备动态的长度存储而已。

text存储的空间=L字符存储实际占用的字节+2Bytes(固定存储2字节的长度)

  • 如果是在MySQL的远古版本4.0以下,那时varchar(M),M指的就是最大字节数。

关于详细的内容,可以参考下表:

MySQL官网 storage-requirements.html#data-types-storage-reqs-strings

M表示的是字符个数

L表示字符的实际字节的长度

【实战用法】

根据需要char,varchar(),text,mediumtext都是可以使用的。

longtext一般也不太可能用吧,真这么大也不存数据库了。

  • varchar(M)的M到底有什么用,既然上面都分析了varchar会动态存储字符串,长度也是根据字符串实际的占用来,那我设置M是否可以随便设置?

varchar(M)的M实际上作用有两个:

(1)M字符数量限制;

(2)作为MySQL加载数据的内存估计使用:Mysql中Varchar(50)和varchar(500)区别是什么? 掘金

MySQL加载数据会根据表的结构定义读取,M会作为每条数据内存估计的一部分,如果读取的数据量太大,预估无法一次加载进内存,就可能会分多次加载进内存,

从而处理速度就降下来了。

因此定义varchar(M)时,M尽可能在满足使用的情况下尽可能小。

  • char与常见的密码长度的设计:

使用bcypt算法密码长度固定60个字符

如果md5就固定30个字符

  • JSON一般存储也会采用字符类型text或者varchar(M),而非MySQL中的原生数据类型JSON.

(2)枚举类型&布尔值

  • 实际上都应该使用tinyint类型来完成。

不得不吐槽一下公司有看到is_delete int(1)的写法,其实完全没有必要,白白浪费了3个字节的。

  • 需要特殊说明一下,tinyint(1)与tinyint(2)在内存上或者物理磁盘上占用的内存是完全一样,都是1个字节。int(1)也不会因为只使用到一个2进制位就占用更少内存,它仍然会占用4个字节。

  • 实际上,数据库MySQL也有自己的enum类型,但这是不采用的。

(3)时间类型

  • 需要存储完整的时间采用datetime

timestamp一般现在不采用了,UTC 2038-01-19 03:14:07.999999就到期了。

  • 我还写过timestamp时间戳转日期的算法,大家感兴趣的话也可以写一些。

timestamp当做一个Long,不考秒以下的单位。提取出有多少天+时分秒

年月日处理:

大概算法就是定义一个月份存储天数的数组。2月特殊处理(需要知道闰年平年的算法,【被4整除&&不被100整除】||【被400整除】为闰年)

时分秒无需处理:直接保留

  • 如果不需要存储时分秒,只要年月日,直接使用date

(4)整数类型

数据库id采用bigint 等于JavaLong

其他看需要使用int 4字节``mediumint 3字节``smallint 2字节``tinyint 1字节

(5)浮点类型

一般只有demical(M,D)是可以用的。float与double是不考虑使用的,因为会丢失精度。

(阿里手册明确禁用float,double)


M为整数位+小数位

D为小数位

0<=M<=650<=D<=30

如demical(5,2)存储的范围为-999.99~999.99

从这里就可以看出来,demical底层是以字符串方式存储的。

映射为Java的BigDemical.(Java使用时需注意BigDemical的一些细节)

【其他细节说明】

1.其他类型其实一般也很少使用,上面基本满足90%以上类型设计的选择了。

前面说到一般不会采用的MySQL原生数据类型有:JSON、Enum。

2.关于【命名、索引等的其他细节】参考阿里巴巴手册就差不多了,这里重点围绕数据类型进行。