说明:此设计规范考虑了业务规范和数据处理的规范,其中的主键ID和etl_update_time字段的规范是为了方便数据平台分析处理数据。
Common
-
不要使用MySQL保留关键字
- 建议级别:强制
- 说明:不论是库名,表名,字段名,索引名等都不要使用MySQL保留的关键字。5.6版本的完整关键词参考MySQL官方文档:https://dev.mysql.com/doc/refman/5.6/en/keywords.html#keywords-5-6-detailed-A
- 反例:from,time,key,like
-
所有对象的名字只能是小写字母或数字或下划线组合,只能以小写字母开头,禁止连续多个下划线
- 建议级别:强制
- 说明:Linux下默认区分大小写;数据库字段名和后端代码映射的时候,后端语言都不允许数字开头的变量出现;连续两个下划线会产生歧义,让人不知道有几个下划线;
- 反例:category__id, spuId, 1vip
- 正例:category_id, spu_id, vip1
-
所有对象的名字必须要见名识意,使用英文单词释意,多个单词使用下划线拼接,不要使用非公认的缩写,不要使用拼音
- 建议级别:强制
- 说明:所有对象的名字要让人看到名字就能知道它大致所表达的意思,不能模糊不清;非公认的缩写和拼音会让人很难读懂
- 反例:product_id, act_id, pingtuan
- 正例:spu_id,activity_id,group_buy
Table
-
临时表的表名以temp_开头;已经弃用的表以dropped_开头;备份表以bak_开头,日期为后缀
- 建议级别:强制
- 说明:为了快速理解和整理,并且在同步数据的时候会自动忽略掉这些表
- 正例:
temp_user
,dropped_user
,bak_user_20210615
-
每个表必须有主键,并且字段名为id,字段类型为int(11) unsigned,自增,步长为1
- 建议级别:强制
- 说明:自增主键ID能有序标识数据唯一性,unsigned标识存储非负数,能提升存储范围;也方便之后的数仓同步,去重等
- 参考SQL如下:
`id` int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键-自增ID'
-
每个表必须有etl_create_time和etl_update_time字段,并设置值为记录的写入或更新时间
- 建议级别:强制
- 说明:这样做方便后面数据的同步,统计,去重等。该字段不具备业务意义,不要在业务逻辑里面使用。代码里面也不需要维护;
- 参考SQL如下:
`etl_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间-etl专用', `etl_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间-etl专用'
-
不允许使用联合主键,主键只能是自增ID,联合主键改成联合唯一索引
- 建议级别:强制
- 说明:由主键ID标识每一条唯一记录,业务上的联合唯一不要用主键来解决,用联合唯一索引,这样保留了主键ID的连续性和唯一性也可以替代联合主键的作用
- 反例:
PRIMARY KEY (column_a,column_b)
- 正例:
PRIMARY KEY (id), UNIQUE KEY uk_a_b (column_a,column_b)
-
非负数的数字类型,必须使用unsigned
- 建议级别:强制
- 说明:unsigned相对于signed可以多出一倍的存储空间,如 signed int -2147483648~2147483647 , unsigned int 0~4294967295
-
所有表中存储相同数据的列类型必须一致
- 建议级别:强制
- 说明:如果查询时关联列类型不一致会自动进行隐式转换,从而导致列上的索引失效
- 反例:a表中 user_id为int ,b表中user_id为string
- 正例:所有表中表示user_id的字段类型相同,都为int或都为string
-
所有的表和字段都需要加注释
- 建议级别:强制
- 说明:为了减少沟通成本,增加可读性,建议给每个表和字段都加上注释
-
不要使用外键
- 建议级别:强制
- 说明:外键与级联的更新操作对性能会有一定影响,并发场景容易产生死锁。外键的逻辑请在服务端实现
-
boolean 类型的数据,设置成 tinyint(1)
- 建议级别:建议
- 说明:1为true,0为false
-
优先选择符合存储需要的最小的数据类型和长度
- 建议级别:建议
- 说明:列的字段越大,建立索引时所需要的空间也就越大,这样一页中所能存储的索引节点的数量也就越少也越少,在遍历时所需要的IO次数也就越多,索引的性能也就越差
-
所有字段定义为 not null,业务中允许null的字符字段可以设置默认值为空
- 建议级别:建议
- 说明:索引对null的列需要额外的空间来保存,会占用更多的空间;SQL查询的时候对null进行比较和计算需要做特殊处理,容易踩坑
- 参考SQL:
NOT NULL DEFAULT ''
-
不要使用自增ID作为业务主键
- 建议级别:建议
- 说明:自增ID在测试,生产环境都是不一样的,而且不好控制。建议业务自己维护自己的业务主键,如订单号
-
枚举字段注释格式 字段的注释 @枚举值1:枚举1意思;@枚举值2:枚举2意思;
- 建议级别:建议
- 说明:枚举注释格式统一,有助于减少理解成本;有助于注释和文档生成
- 正例:gender comment ‘性别 @M:男; @F:女; @N:未知’
Index
-
索引命名规范,唯一索引以 uk_ 开头,普通索引以 idx_ 开头
- 建议级别:强制
- 说明:uk 是唯一索引 unique key 的缩写,idx 是普通索引index的缩写
-
确保多表join查询的时候,所关联的字段必须要有索引
- 建议级别:强制
- 说明:不仅要保证关联字段要有索引,还有确保关联字段类型必须一致,否则会导致索引失效
-
业务上具有唯一特性的字段,即使是组合字段,也需要建唯一索引
- 建议级别:推荐
- 说明:唯一索引在insert时候的速度损耗可以忽略,但极大的提高了查询的效率;而且唯一索引能够完全确保业务的唯一性。
-
尽量使用覆盖索引来查询,避免回表
- 建议级别:推荐
- 说明:覆盖索引可以在一棵树上就找到需要的列的数据,无需回表,速度更快。
- 正例:explain的输出结果中Extra字段为Using index时,能够触发覆盖索引
SQL
-
禁止使用不含字段列表的insert语句
- 建议级别:强制
- 说明:当表结构变更的时候,会带来不可用的影响
- 反例:insert into values (‘a’,‘b’,‘c’);
- 正例:insert into t(c1,c2,c3) values (‘a’,‘b’,‘c’);
-
禁止使用SELECT * ,必须显式SELECT 字段列表
- 建议级别:强制
- 说明:select * 会消耗更多的资源;select * 无法使用覆盖索引;列出详细字段也可减少表结构变更带来的风险
-
where语句中禁止对列进行函数操作或计算
- 建议级别:强制
- 说明:对列进行函数操作或计算会导致索引失效
- 反例:age / 10 = 3; substring(name,1,3)=‘abc’; date_format(create_date, ‘%Y-%m-%d’) = ‘2022-01-03’
- 正例:age = 10 * 3; name like ‘abc%’; create_date between ‘2022-01-03 00:00:00’ and ‘2022-01-03 23:59:59’
-
在确认没有重复值的情况下使用union all 代替 union
- 建议级别:强制
- 说明:union会把两个结果集放到临时表,然后再进行去重,union all 则不会
-
使用join代替子查询
- 建议级别:强制
- 说明:子查询的效率通常较低,子查询的结果集会被存放到临时表中,而临时表是没有索引的。
-
查询语句中有别名的,
as
不要省略- 建议级别:建议
- 说明:有
as
隔开字段和别名,SQL语句更容易理解。
附录1 常用字段
id int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键-自增ID'
etl_create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间-etl专用'
etl_update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间-etl专用'
is_deleted tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除 @1:是; @0:否'