数据库设计规范
数据库设计之三大范式
-
要求表的每个字段必须是一个不可分割的独立的单元
-
在第一范式的基础上,要求每张表只表达一个意思。表中的每个字段都和主键有依赖
-
在第二范式基础,要求每张表的主键之外的其他字段都只能和主键有直接的决定依赖关系
-
数据库结构设计
逻辑设计----->物理设计
-
实际工作
逻辑设计 + 物理设计
-
物理设计
表名 字段名 字段类型
规范制定
-
一、数据库命名规范
- 所有的数据库对象名称必须使用小写字母,并且使用下划线分割,因为mysql在linux系统下是大小写敏感的。
- 所有的数据库对象名称禁止使用mysql保留关键字,如果一定要使用,则需要使用
from
`` 括起来。 - 数据库对象的命名要做到见名识义,并且最好不要超过32个字符,例如 用户数据库:yy_userdb,用户账号表:user_account
- 所有的临时表必须以tmp_ 开头,备份表必须以bak_ 开头,并以日期为后缀,例如tmp_user_20190721,便于标识表,和后面清理数据作为依据
- 所有的存储相同数据的列名和列类型必须一致,一般是作为关联列使用,如果类型不一样,在做关联的时候,会做隐式的列类型转换,同时也会造成列上的索引失效,同时查询效率大幅降低
-
二、数据库基本设计规范
-
所有表必须使用Innodb存储引擎。Mysql5.5使用之前Myisam(默认存储引擎)情况(不支持事务),。原因:-1. 5.6版本后默认Innodb引擎 -2. Innodb支持事务,行级锁,更好的恢复性,高并发下性能更好。
-
数据库和表的字符集统一使用UTF8。兼容性更好,如果要存储emoji等字符表情的话则对单独列使用utf8mb4,可以避免由于字符集转换产生的乱码。Mysql中utf8字符集汉字点3个字节 ,ASCLL码占用1个字节
-
所有表和字段都添加注释
-
使用comment从句添加表和列的备注
-
从一开始就进行数据字典的维护
-
-
尽量控制单表数据量大小,建议控制在500W以内
-
500w并不是Mysql的数据库限制,这种限制取决于存储设置和文件系统
-
谨慎使用Mysql分区表,可以用历史数据归档,分库分表等手段来控制数据量大小
- 谨慎使用MySQL分区表:分区表在物理上表现为多个文件,在逻辑上表现为 一个表
2. 谨慎选择分区键,跨分区查询效率可能更低
3. 建议采用物理分表的方式管理大数据
-
-
尽量做到冷热数据分离,减小表的宽度
减少磁盘IO,保证热数据内存缓存命中率
利用更有效的利用缓存,避免读入无用的冷数据
经常一起使用的列放在一个表中
-
禁止在表中建立预留字段(大多数开发都会犯的问题)
预留字段的命名很难做到见名识义
预留字段无法确认存储的数据类型,所以无法选择合适的类型
对预留字段类型的修改,会对表进行锁定
-
禁止在数据库中存储图片、文件等二进制数据,一定要使用的话,推荐使用blob,但是过多的使用这种类型会影响数据库的性能。
-
禁止在线上做数据库压力测试,要切换到压力测试服务器来做专门的测试,对比生产环境的硬件进行评估。
-
禁止从开发环境,测试环境直接生产环境数据库
-
-
三、数据库索引设计规范
索引对数据库的查询性能来说是非常重要的,是一把双刃剑
-
限制每张表的索引数量,建议单张笔索引不超过5个:索引并不是越多越好!索引可以提高效率同样也可以降低效率;
-
索引可以增加查询效率,也会降低插入和更新的效率(mysql查询优化器根据一定的规则/信息生成最优执行计划)
-
禁止给表中的每一列都建立单独的索引:
-
-
每个Innodb表必须有一个主键:Innodb是按照哪个索引的顺序来组织表的呢?答案:主键 (Innodb是索引组织表)
-
不使用更新频繁的列作为主键,不使用多列主键(联合主键)
-
不要使用UUID,MD5,HASH,字符串列作为主键
-
最好选择可以保证顺序、并且非空的列作为主键:使用自增ID值
-
-
常见索引列的建议:
-
SELECT、UPDATE、DELETE 语句的WHERE从句中的列,包含在ORDER BY、GROUP BY、DISTINCT中的字段
-
多表JOIN的关联列
-
-
如何选择索引列的顺序
-
区分度最高的列放在联合索引的最左侧:主键和唯一索引
-
尽量把字段长度小的列放在联合索引的最左侧
-
使用最频繁的列放在联合索引的左侧
-
-
避免建立冗余索引和重复索引
重复索引:primary key(id)、 index(id) 、unique index(id)
冗余索引:index(a,b,c)、 index(a,b)、index(a) 对于a列来说就是冗余索引
-
对于频繁的查询优先考虑使用覆盖索引:包含了所有查询字段的索引
对于select * 是不可能使用覆盖索引的
-
避免Innodb表进行索引的二次查找
-
可以把随机IO变为顺序IO加快查询效率
-
-
尽量避免使用外键
不建议使用Innodb本身的外键约束,在表与表的关联键上建立索引则是必须的
-
外键可用于保证数据的参照完整性,但建议在业务端实现
-
外键会影响父表和子表的写操作从而降低性能
-
-
-
四、数据库字段设计规范
字段类型的选择,会直接影响数据库的性能
-
优先选择符合存储需要的最小的数据类型
-
将字符串转化为数字类型存储:15字节------4字节
INET_ATON(
255.255.255.255
) = 4294967295INET_NTOA(4294968395) =
255.255.255.255
对于非负型的数据来说,要优先使用无符号整形来存储:无符号相对于有符号可以多出一倍的存储空间,对于非负数采用无符号整形进行存储
SIGNED INT -2147483648~2147483648
UNSIGNED INT 0~4294967295
-
VARCHAR(N) 中N代表的的是字符数,而不是字节数
-
使用UTF8存储汉字Vachar(255)=765个字节
-
过大的长度会消耗更多的内存
-
-
尽量在表中使用TEXT/BLOB类型
-
避免把BLOB或TEXT列分离到单独的扩展表中
-
TEXT或BLOB类型只能使用前缀索引,且不能添加默认值
-
避免使用ENUM数据类型 只能存65535种类型:修改ENUM值需要使用alter语句,存在操作风险
-
ENUM类型的order by操作效率低,需要额外的转换(转换int类型)操作
-
禁止使用数值作为ENUM的枚举值
-
-
尽可能把所有列定义为 not null
- 索引 NULL列需要额外的空间来保存,所以要占用更多的空间
- 进行列比较和计算时要对NULL值做特别的处理,进而导致索引失效
-
使用TIMESTAMP或DATETIME类型存储时间
-
timestamp
字符串存储日期型数据(不正确的做法)
缺点1:无法用日期函数进行比较和计算
缺点2:用字符串存储日期要占用更多的空间
TIMESTAMP1970-01-01 00:00:01~2038-01-19 03:14:07,TIMESTAMP占用4字节和INT相同,但比INT可读性高
超出TIMESTAMP则使用DATETIME类型
-
同财务相关的金额类数据,必须使用decimal类型(商品价格、成本)
- 非精准浮点:float、double
- 精准浮点:decimal
- Decimal类型为精准浮点数,在计算时不会丢失精度,占用空间由定义的宽度决定
- 可用于存储比bigint更大的整数数据
-
-
五、数据库SQL开发规范
-
建议使用预编译语句进行数据库操作
- 只传参数,比传递sql语句更高效
- 相同语句可以一次解析,多次使用,可以有效的避免sql注入的问题
-
避免数据类型的隐式转换:列定义的类型和传递的参数类型不一致
- 隐式转换会导致索引失效:select name.phone from customer where id='111'
-
充分利用已存在的索引,而不是盲目的去创建索引
- 避免使用双%号的查询条件,如 a like '%123%' 或者 a like '%123',后置的%是可以利用到列上的索引的
- 一个SQL只能利用到复合索引中的一列进行范围查询
- 使用left join或者not exists来优化not in操作
-
程序链接不同的数据库使用不同的账号,禁止跨库查询
- 为数据库迁移和分库分表留出余地
- 降低业务耦合度
- 避免权限过大而产生安全的风险
-
禁止使用SELECT * 必须使用SELECT <字段查询>
- 消耗更多的CPU和IO以及网络带宽资源
- 无法覆盖索引
- 可以减少表结构的变更带来的影响
-
禁止使用不含字段列表的INSERT语句
例如 insert into t values('a','b','c'); 纠正:insert into t (a,b,c) values('a','b','c');
可减少表结构的变更带来的影响
-
避免使用子查询,可以把子查询优化为join操作
- 子查询的结果集无法使用索引
- 子查询会产生临时表操作,如果子查询数据量大则严重影响效率
- 消耗过多的CPU及 IO资源
-
避免使用JOIN关联太多的表
- 每join一个表会多占一部分内存(join_buffer_size)
- 会产生临时表操作,影响查询效率
- Mysql最多允许关联61个表,建议不超过5个
-
减少同数据库的交互次数
- 数据库更适合处理批量操作
- 合并多个操作到以,可以提高处理效率 比如:alter table t1 add column c1 int,change column c2 int ....
-
使用in代替or: in的值不要操作500个,in操作可以有效的利用索引
-
禁止使用order by rand()进行随机排序
- 会把表中所有符合条件的数据装载到内存中进行排序
- 会消耗大量的CPU IO以及内存资源
- 推荐在程序中获取一个随机值,然后从数据库中获取数据的方式
-
WHERE从句中禁止对列进行函数转换和计算
- 对列进行哈数转换或者计算胡导致无法使用索引,例如:where date(createtime) = '20160901' ,改进:where createtime >='20190901' and createtime < '20190902'
-
在明显不会有重复值使用UNION ALL而不是UNION
- UNION会把所有的数据放到临时表中后在进行去重操作
- UNION ALL不会在对结果集进行去重操作
-
拆分复杂的大SQL为多个小SQL
- Mysql一个SQL只能使用一个CPU进行计算
- SQL拆分后可以通过并行执行来提高处理效率
-
-
六、数据库操作行为规范(面向运维)
-
超100万行的批量写操作,要分批多次进行操作
- 大批量的操作可能会造成严重的主从延迟
- binlog日志为row格式时产生大量的日志
- 避免产生大事务的操作:阻塞,占用数据库链接
-
对大表数据结构修改一定要谨慎,会造成严重的锁表操作。尤其是生产环境,是不能忍受的。
- 对于大表使用pt-online-schema-change工具修改表结构:可以避免大表修改的主从延迟,同时避免了对标字段进行修改时进行锁表
-
禁止程序使用的账号赋予super权限
- 当达到最大连接数限制时,还允许一个有super权限的用户连接
- super权限只留给DBA处理问题的账号使用
-
对于程序连接数据库账号,遵循权限最小原则
- 程序使用数据库账号只能在一个DB下使用,不准跨库
- 程序使用的账号原则上不允许drop权限
-