(一)建表规约
1.【强制】在编写建表语句时,需同时考虑回滚、以及必要的初始化语句;生产环境变更表或数据时须提供变更和回滚sql文件。
说明: 以"项目名_版本号_Create.DDL"、"项目名_版本号_RollBack.DDL、"项目名_版本号_InitData.DML",
分别表示创建、回滚、初始化数据的sql文件放在项目工程的对应文件夹中。
2.【强制】数据库版本统一为MySQL5.6,引擎统一使用InnoDB。
3.【强制】库名、表名、字段命名,必须使用小写字母或数字组成,库名应与应用名称一致、表或字段名以英文单词加"_"
表示其用途/目标/意义来命名,表名、字段名不超过32个字符、表字段数量不超过35个。
正例:poseidon(库名)、send_task(表名),send_task_logs_201710(表名)、user_login_name(字段名)
4.【强制】主键索引名为 pk_字段名、唯一索引名为 uk_字段名、普通索引名则为 idx_字段名,索引数量不超过5个。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称
5.【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
6.【参考】业务数据需要进行简单的归类,但相对要支持灵活变化,应使用数据字典表或通过统一服务的形式获取,
禁止通过distinct语句在业务数据表去重获取,禁止将归类写死在业务处理类和方法中。
7.【强制】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
说明:设计时尽量使用少的空间存储字段,如能用int的就不用char或者varchar、能用tinyint的就不用int 、
能用varchar(20)的就不用varchar(255)。
8.【推荐】关于char、varchar、text应根据是否定长、最大支持长度、效率等因素合理选择类型,建议为:
1) 经常变化的字段用varchar
2) 知道固定长度的用char
3) 尽量用varchar
4) 超过255字节的只能用varchar或者text
5) 能用varchar的地方不用text
6) 如超长(如5000)定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段执引效率。
9.【禁止】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
10.【禁止】小数类型为 decimal,禁止使用 float 和 double。
说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不
正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
11.【强制】表必备三字段:id, create_time, modified_time。
说明:其中 id 必为主键。create_time表示主动创建, modified_time过去分词表示被动更新。
12.【强制】时间格式统一用yyyy-MM-dd HH:mm:ss.fff,精准到毫秒;存储、传输统一用北京时间,
如有其它时区时间(如下单、付款等时间)保留原始时间、转换的原则为谁用谁转换。
13.【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1)不是频繁修改的字段。
2)不是 varchar 超长字段,更不能是 text 字段。
正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存
储类目名称,避免关联查询。
14.【推荐】单表行数超过 100 万行或者单表容量超过 1GB,才推荐进行分库分表分区。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表分区。
15.【强制】表、字段注释(COMMENT)需遵守以下注释约束,如有修改字段含义或对字段表示的状态追加时,
需要及时更新字段注释以及《数据库设计说明书》,注释格式参考:
1) 表/视图注释格式为:用途|负责人|创建日期 ,如:记录用户身份证号码|张三|2017-12-28
2) 字段注释格式为:用途|说明 , 如:记录用户性别|1男,2女,3未知(李四 2017-12-28 新增)
3) 关联字段来源需要说明,来自哪张表的那个字段,如:取自eload_user.user_id
16.【禁止】禁止定义枚举类型的表;因为在使用canal内核的产品无法拉到数据
17.【强制】字符类型的存储禁止用null,用""代替;如有将字段和值拼接成字符串存储的需要,统一先转json格式存储。
(二)索引规范
1.【推荐】ID、关联字段、业务外键、create_time 字段推荐建索引,在选择性高的字段创建索引,注意组合索引的顺序,
利用索引的最左原则,使用复合索引,而不是添加新的索引、避免冗余索引。
反例:idx_a_b_c(a,b,c)、idx_a(a)、idx_a_b(a,b)
2.【推荐】业务上具有唯一特性的字段,且具有强唯一性要求。推荐建成唯一索引。
说明: 不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;
另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
示例:如身份证号码,在业务层面已经做了唯一性检查,但在表设计时还是要加唯一索引约束。
3.【推荐】原则上超过三个表禁止 join,如超过三张以上表join须集体评审讨论通过。需要 join 的字段,
数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表 join 也要注意表索引、SQL 性能。
4.【推荐】在 varchar 字段上建立索引时,建议指定索引长度,没必要对全字段建立索引,根据
实际文本区分度决定索引长度即可。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分
度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
示例:如varchar(1024),索引长度为36时区分度就有 90%以上,就没必须建立1024长度的索引。
5.【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
6.【推荐】如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合
索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引中有范围查找,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引a_b 无法排序。
7. 【推荐】利用覆盖索引来进行查询操作,避免回表。
说明:如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作
说明:使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,
MySQL查询优化器在执行查询前会决定是否有索引覆盖查询
8. 【推荐】利用延迟关联或者子查询优化超多分页场景。
说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回
N 行,当 offset 特别大的时候,效率会非常的低下。
正例:通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据。
9. 【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好。
说明: 以下列出的12种级别,等级依次降低:
1) system:系统表,表只有一行记录。这是const表连接类型的一个特例;
2) const: 表中最多只有一行匹配的记录。读常量,由于只有一行记录,优化程序里该行记录的字段值可以被当作是一
个常量,仅在查询开始时读取一次。当PRIMARY KEY或UNIQUE索引与常量进行比较时会显示const,速度非常快;
3) eq_ref:类似const,const比较的是常量,eq_ref是同另一张表中的字段关联比较,这是最好的连接类型,
eq_ref出现在PRIMARY KEY或UNIQUE类型索引进行”=”做比较时检索字段,
比较的值可以是固定值或者是表达式,表达示中可以使用表里的字段;
4) ref:查询时的索引类型不是PRIMARY KEY或UNIQUE类型索引导致匹配到的行有多个,或者仅能用到索引的
左前缀而非全部时的访问类型,ref可被用于基于索引字段进行 - 或 <=> 操作;
5) ref_or_null:与ref的唯一区别就是在使用索引引用的查询之外再增加一个空值的查询。这种连接类型类似ref,
不同的是MySQL会在检索的时候额外的搜索包含NULL值的记录,它经常用于子查询。
6) index_merge:查询中同时使用两个(或更多)索引,然后对索引结果进行合并(merge),再读取表数据;
7) unique_subquery: 子查询中的返回结果字段组合是主键或唯一约束。用于in比较操作符中的子查询锦绣谷的
“键值唯一”的访问类型场景中,如value in (select primary_key from table where x = x)
8) index_subquery:子查询中的返回结果字段组合是一个索引(或索引组合),但不是一个主键或唯一索引,
这种连接类型类似unique_subquery。它用子查询来代替IN,不过它用于在子查询中没有唯一索引的情况下。
9) range:索引范围扫描 。只有在给定范围的记录才会被取出来,利用索引来取得一条记录,
常见于between、<、>等的查询
10) index:全索引扫描 。连接类型跟ALL一样,不同的是它只扫描索引树。它通常会比ALL快点
因为索引文件通常比数据文件小。MySQL在查询的字段只是单独的索引的一部分的情况下使用这种连接类型;
11) fulltext:用于full text索引中用纯文本匹配的方法来检索记录;
12) all:全表扫描。
10. 【强制】建组合索引的时候,区分度最高的在最左边。
正例:如果 where a=? and b=? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where a>?
and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。
11. 【禁止】以下情况不走索引,在编写sql或建索引时需慎重考虑:
1) 条件字段选择性弱,查出的结果集较大,不走索引;
2) where条件等号两边字段类型不同,不走索引;
3) 优化器分析的统计信息陈旧也可能导致不走索引;
4) 索引字段 is null 不走索引;
5) 对于count(*)当索引字段有not null约束时走索引,否则不走索引;
6) like 后面的字符当首位为通配符时不走索引;
7) 使用不等于操作符如:<>、!= 等不走索引;
8) 索引字段前加了函数或参加了运算不走索引;
(三)SQL语句
1.【强制】重要的业务数据、配置数据等在做删除处理时,推荐使用删除标记做逻辑删除、不使用delete做物理删除;
禁止update和delete语句后面没有带where条件。
2.【推荐】复杂的业务逻辑的判断或操作,建议在业务代码中分步骤拆分编写,如果一条sql写完,
会造成该sql语句过于复杂庞大,可读性可维护性下变差,性能一般也不会太高。
3.【强制】sql语句应按照业务模块等分文件统一写在xml或公用类中,禁止sql语句与业务代码混写在一起。
4.【禁用】"<>"、"!="、"not in"、"exsits"和"not exists"等反逻辑运算符原则上禁用。
说明:"<>"、"!="、"not in"可用"="和"in"代替;"exsits"、"not exists"可用"等连接"和"外连接"
5.【推荐】关于or、in、union all运算符的选择,建议同一字段,将or改写为in,不同字段,将or改为union all
6.【强制】sql语句中的where子句中的每个条件的操作符两边类型应该相等,禁止潜在的数据类型做转换。
7.【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的
标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
8.【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct
col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
9.【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为
NULL,因此使用 sum()时需注意 NPE 问题。
正例:可以使用如下方式来避免 sum 的 NPE 问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
10. 【强制】使用 ISNULL()来判断是否为 NULL 值。
说明: NULL 与任何值的直接比较都为 NULL。
1) NULL<>NULL 的返回结果是 NULL,而不是 false。
2) NULL=NULL 的返回结果是 NULL,而不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。
11.【强制】在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。
12.【禁止】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 student_id是主键,那么成绩表中的 student_id
则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为
级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻
塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
13.【禁止】禁止使用存储过程、触发器、游标,这些难以调试和扩展,更没有移植性。
14.【强制】数据订正(特别是删除、修改记录操作)时,要先确认,避免出现误删除,确认无误才能执行更新语句。
示例:如A、B两个线程都读取某条数据其中一个字段值(如1000)做加100操作,A线程将值更新为1100,
B线程如果不加判断,结果也会更新也为1100,正确的结果值应为1200,可通过增加版本号等字段来控制。
15.【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
16.【强制】数据库编码格式、所字符存储与表示,均以 utf-8 编码,注意字符统计函数的区别。
说明: SELECT LENGTH("轻松工作"); 返回为 12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为 4