在设计表和字段时,对具体的细节老是不确定。这里作为经验记录帖,方便查询,随时更新
字段
1. 字段类型
尽量选择存储空间小的字段类型
- 整数类型分为 tinyint,smallint,int,bigint 长度分别 1,2,4,8字节
- BOOLEAN 在 MySQ 里的类型为 tinyint(1)
- int(30) 中的数字 30 表示整数值在查询结果中显示的字符宽度,不影响存储范围或分配的存储空间。
- int(20) 能存储数字的范围为 int 类型可以存储的范围,占用 4 个字节的存储空间。
- 小数使用 DECIMAL(5,2),表示值的范围是从 -999.99 到 999.99
- 如果小数位存储4位,那么修改数据的时候传入的 5 位,那么第五位数字在存储的时候遵循四舍五入规则。
- 如果数值在其取值范围之外,则直接报 Out of range value 错误。
- 字符类型有两种,char 是定长字符串类型,varchar 是可变长度字符串类型
- char 的上限为 255 字节,varchar 的上限 65535 字节,text 的上限为 65535。同一张表中,所有 varchar 字段的长度加起来不能大于 65535。
- char(n)和 varchar(n)中括号中 n 代表字符的个数,并不代表实际存储的字节数或整数值的范围。汉字在 UTF-8 编码下通常占用 3 个字节,char(n) 可以存放 n / 3 个汉字。
- char在存储的时候会截断尾部的空格,varchar 在存储时超过范围也会插入报错。
- varchar 会使用 1-3 个字节来存储长度,text 不会。
2. 字段长度
Mysql 字段定义的长度分为字符长度和字段长度
在 mysql 中,varchar 和 char 类型表示字符长度,而其他类型表示的长度都表示字节长度。比如 char(10)表示字符长度是 10,而bigint(4)表示显示长度是4个字节,但是因为 bigint 实际长度是8个字节,所以 bigint(4)的实际长度就是 8 个字节。
我们在设计表的时候,只需要考虑 char 和 varchar 的字符长度,一般设置为 2 的幂次方
3. 主键设计
主键最好不要与业务逻辑有所关联。比如身份证,虽然是唯一的,但是不建议。主键最好是毫无意义的一串独立不重复的数字:
- UUID
- Auto_increment自增的主键
- 雪花算法生成的主键。
4. 优先考虑逻辑删除,而不是物理删除
- 物理删除:把数据从硬盘中删除
- 逻辑删除:给数据添加一个字段,比如 is_deleted,标记该数据已经逻辑删除。
# 物理删除
delete from account_info_tab whereaccount_no ='666';
# 逻辑删除
update account_info_tab set is_deleted = 1 where account_no ='666';
增加状态字段,更变它用来控制数据
5. 每个表都需要添加这几个通用字段
一般来说必须字段:
id:主键,一个表必须得有主键,必须
create_time:创建时间,必须
modifed_time/update_time: 修改时间,必须,更新记录时,需要更新它
version : 数据记录的版本号,用于乐观锁,非必须
remark :数据记录备注,非必须
modified_by :修改人,非必须
creator :创建人,非必须
6. 一张表的字段不宜过多
- 一张表字段尽量不要超过 20 个字段。如果一张表的字段过多,表中保存的数据可能就会很大,查询效率就会很低。如果实在需要很多字段:
- 可以把一张大的表,拆成多张小的表,它们的主键相同即可。
- 将表分成两张表,一张作为条件查询表,一张作为详细内容表。
7. 尽可能使用 not null 定义字段
如果将字段默认设置成一个空字符串或常量值,并没有什么不同,且都不会影响到应用逻辑, 那就可以将这个字段设置为 NOT NULL。
- 首先,NOT NULL 可以防止出现空指针问题。
- 其次,NULL值存储也需要额外的空间的,它也会导致比较运算更为复杂,使优化器难以优化 SQL。
- NULL值有可能会导致索引失效
8. 设计表时,评估哪些字段需要加索引
如果数据量过少,就没有必要添加索引。否则设计表的时候,如果有查询条件的字段,一般就需要建立索引。但是索引不能滥用:
- 不要创建太多索引,一般单表索引个数不要超过5个。因为创建过多的索引,会降低写得速度。
- 区分度不高的字段,不能加索引,如性别等
- 索引创建完后,避免索引失效的情况,如使用 mysql 的内置函数,会导致索引失效的
- 索引过多的话,可以通过联合索引的话方式来优化。然后的话,索引还有一些规则,如覆盖索引,最左匹配原则等等
下表很可能会有根据 user_id 或者 name 查询用户信息,给 user_id 加上唯一索引,name 加上普通索引
CREATE TABLE user_info_tab (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`create_time` datetime NOT NULL,
`modifed_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE,
UNIQUE KEY un_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
9. 不需要严格遵守 3NF,通过业务字段冗余来减少表关联
数据库三范式(3NF):
第一范式:对属性的原子性,要求属性具有原子性,不可再分解
第二范式:对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖
第三方式:对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖
设计表及其字段之间的关系, 应尽量满足第三范式。但是有时候,可以适当冗余,来提高效率。比如以下这张表:
商品名称 | 商品型号 | 单价 | 数量 | 总金额 |
---|---|---|---|---|
手机 | 华为 | 8000 | 5 | 40000 |
总金额这个字段的存在,表明该表的设计不满足第三范式,因为总金额可以由单价 * 数量得到,说明总金额是冗余字段。但是,增加总金额这个冗余字段,可以提高查询统计的速度,这就是以空间(增加列)换时间的作法。
10. 选择合适统一的字符集
MySQL 支持的字符集有 utf8、utf8mb4、GBK、latin1 等:
-
latin1:MySQL 默认字符集,1 个字节长度。(MySQL 8.0 之前默认)
-
utf8:支持中英文混合场景,国际通过,3 个字节长度
-
utf8mb4: 完全兼容 utf8,4 个字节长度,一般存储emoji 表情需要用到它。(MySQL 8.0 之后默认)
-
GBK :支持中文,但是不支持国际通用字符集,2 个字节长度
11. 时间的类型选择
我们设计表的时候,一般都需要加通用时间的字段,如 create_time、modified_time 等等。
优先使用 datetime 类型来保存日期和时间,因为存储范围更大,且跟时区无关。
12. 大字段
设计表的时候,我们尤其需要关注一些大字段,即占用较多存储空间的字段。比如用来记录用户评论的字段,又或者记录博客内容的字段。如果直接把表字段设计成 text 类型的话,就会浪费存储空间,查询效率也不好。
这种非常大的数据,可以保存到mongodb中,然后,在业务表保存对应 mongodb 的 id 即可。这种设计思想类似于,我们表字段保存图片时,为什么不是保存图片内容,而是直接保存图片 url 即可。
13. SQL 编写的一些优化经验
一些遇到的 SQL 优化经验:
-
查询 SQL 尽量不要使用 select *,而是 select 具体字段
-
如果知道查询结果只有一条或者只要最大/最小一条记录,建议用 limit 1
-
应尽量避免在 where 子句中使用 or 来连接条件
-
注意优化 limit 深分页问题
-
使用 where 条件限定要查询的数据,避免返回多余的行
-
尽量避免在索引列上使用 mysql 的内置函数
-
应尽量避免在 where 子句中对字段进行表达式操作
-
应尽量避免在 where 子句中使用 != 或 <> 操作符
-
使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则。
-
对查询进行优化,应考虑在 where 及 order by 涉及的列上建立索引
-
如果插入数据过多,考虑批量插入
-
在适当的时候,使用覆盖索引
-
使用 explain 分析你 SQL 的计划