1. 建表规约
- 必须使用InnoDB表。
说明:InnoDB引擎可以提供类似Oracle的性能和数据安全性;
禁止:禁止使用MyISAM表。
- 表一定要有主键索引,且主键索引应该是业务主键,不要使用MySQL自增主键,不要使用超过64字符的字符串。
说明:主键用来约束唯一的记录,便于数据的管理,业务主键避免了自增主键不同机房的同步问题,可以实现全局唯一;
建议:尽量使用整型来充当主键,这样会在索引组织表上获得更高的效率;
禁止:不要使用超过64字符的字符串作为主键索引,MySQL对字符串索引的长度有要求,且和字符集相关。
- 表名,索引名不要超过30个字符。
说明:过长的表名可读性较差,命名做到见名知意即可,亦有中文注释作为补充。
- 数据表采用T开头,表示为数据表,视图以V开头,表示为视图。命名应该符合下面的范式:[T/V][Service][BusiDesc]。
说明:Service代表此表或者视图属于何种微服务,BusiDesc表示该表的具体用途;
示例:T_User_UserInfo,表示User服务的用户信息表;
建议:BusiDesc尽量使用短名称但要表意明确,单词之间用开头用大写字母。
- 字段名采用驼峰式命名,但是首字母要大写,禁止出现数字开头。
- 禁止使用MySQL保留字,具体的保留字参考MySQL官方文档。
说明:https://dev.mysql.com/doc/refman/5.7/en/keywords.html
反例:使用Desc作为Description的简写,Desc是MySQL的保留字。
- 主键索引的命名使用P开头加字段名称的方式;唯一键命名采用U开头加字段名称的方式;普通索引命名采用I_开头加字段名称的方式。
- 对于可以确定长度的字符串,使用char(N),不要使用varchar(N)。
说明:char是定长字段,适用于长度固定的字段,且符合最小适用原则;varchar类型需要额外占用一部分的空间作为变长的的说明,因此不适用于定长的字段。
正例:性别一般只有两种,M或者F,考虑使用char(1);
反例:对于上述定长的性别列,使用varchar(1)。
- varchar字段长度不要超过4000,超过4000考虑使用blob。
说明:blob字段属于行溢出字段,非超长情况下不要使用。
- 保证单一属性原则。
说明:一个字段内只存一种信息,保证单一属性;
示例:订单的用户信息,如果一个订单对应多个用户,要建立一张订单号和用户id的对应关系表;
反例:新建一个字段,用于保存所有的用户id,id之间用逗号分隔。
- 库名和服务名称尽量一致。
- 每个字段都要有汉字注释。
- 库默认采用utf8mb4字符集,没有特殊情况不要自定义表和字段的字符集。
说明:考虑到向前兼容的需求,将默认字符集设置为MySQL8的默认字符集utf8mb4。**
- 不必完全采用第三范式设计表,允许以适当的冗余提升查询效率。
说明:完全的第三范式设计不会有任何冗余字段,但是有时候对于简单的业务不需要关联的,表内冗余一些信息反而更加高效。
- 保证最小适用原则,字段类型选择时选择能正常使用的最小的字段。
示例:人的寿命一般在100岁左右,可以使用无符号的tinyint来表示人的年龄,区间范围从0到255。
反例:表示是否的字段只有0或者1,字段类型创建为bigint。
- 不创建外键,外键会让删除操作变得极为困难。
- 默认值不要使用空字符串,空字符串可以认为是null。
示例:如果预见到该字段可能出现空值,则允许此列为空;
反例:默认值设置为’’,加上非空约束。
- 考虑使用触发器之类的工具实现表的实时更新,取代视图的使用。
说明:视图查询的效率低于表,MySQL没有实现物化视图,可以利用触发器实现类似于物化视图的表;
建议:查询较为简单的时候,可以将查询封装成视图,简化Java代码的编写难度。
禁止:特别复杂的查询禁止封装成视图。
- 不要使用MySQL专有的数据类型,如text等。
说明:要考虑到代码未来存在平台移植的可能,要是用标准的类型。
2. 索引规约
- 可以确定唯一记录的列,必须组成唯一索引。
说明:唯一索引的好处是显然的,可以提供接近主键索引的查询速度,且能够有效地提升并发能力,降低CPU占用。
- 关联查询的关联条件,应该保证类型一致,如果是字符串类型,则应该保证字符集一致。
说明:关联条件的类型不同,会导致隐式类型转换,无法利用到既有索引,字符集不一致也存在该问题
- 建议在varchar字段上建立部分索引,即指定只索引字段的前N个字符。
说明:大部分的varchar字段前20个字符就能区分开不同的记录,因此没有必要对整个字段进行索引,缩短索引长度也能提高效率。
- 只检索需要的列,尽量避免select *的使用。
说明:业务查询一般是固定的,对于一些查询实际上可以利用到索引覆盖技术,减少IO消耗,这要求查询的列是索引中的索引列,具体的索引覆盖技术可以参考《高性能MySQL》相关章节。
- 分页尽量使用预读来提升效率。
说明:limit 100,10并不是从第100条记录开始读取10条,而是读取110条记录,选择最后10条,因此分页效率很低,建议在业务端采用预读方式进行优化。
- 建立索引的时候,选择度最高的列放在最左边。
说明:B+树索引对于选择度高的列更有效,而联合索引的组织顺序合理可以有效地降低树的高度,降低IO成本
- 建立索引的时候掌握最左前缀原则,设计合理的组合索引以减少索引冗余。
说明:根据最左前缀原则,一个建立在a,b,c列上的联合索引(a,b,c),可以满足以下几种查询:where a=? and b=? and c=?,where a=? and b=?,where a=?。
3. SQL规约
- 除非业务需要,不要使用count(列名),这样会忽略掉这个列为null的行,造成统计结果不准确。
说明:count(*)效率很高,优化器会自动选择辅助索引进行优化,不存在效率问题。
- 正确区分inner join和left join,非必要情况下不要使用左连接,会出现效率问题。
说明:左连接会做一次笛卡尔积,然后将悬浮元组进行筛选后得到结果,比单纯的集合交运算复杂且低效。
- 避免在in子句内使用大量的元素。
说明:in子句会被优化器改写成union all或者or,效率非常低。
- 避免派生表的使用,用join取代派生表。
说明:所谓派生表查询select a.x, b.y from a inner join (select y from c) b on a.y = b.y; 这个SQL实际上可以改写成select a.x, b.y from a inner join c where a.y = c.y; MySQL在对派生表的处理上效率并不高。
- 删除全表数据采用truncate不要用delete,truncate可以在瞬间删除大表的所有数据,delete要写日志,效率较低。
说明:truncate语句不会产生undo信息,因此没有办法进行数据的回滚,要格外注意。
- 不要使用自定义函数。
说明:自定义函数可能会出现因为测试不充分暴露的bug,大部分自定义函数都有性能问题。
- 对于必须要写出的完成业务使用的特别复杂的SQL,要考虑业务设计是否合理。
- 除了OLAP应用之外,OLTP禁止使用存储过程。
- 不建议使用左模糊或者全模糊查询,这样不会用到索引。
-
4. Java开发数据库相关代码规约
使用JPA编写查询语句时,同样要注意不要使用*去查询所有的字段,需要查哪些则写哪些。
- 使用绑定变量,禁止使用拼接字符串形式。
说明:代码中的变量部分应该采用占位符,例如jdbc代码使用?作为占位符,HQL使用:{param}作为占位符
反例:定义这样的SQL字符串:String sql = “select a, b from t where c = “ + in_param;
- 不要随意更新查询的结果类。
说明:更新查询结果类中的属性,会导致事务结束时自动更新这条记录。
- 在正确的位置使用@Transactional注解。
说明:事务是一个重要的概念,要考虑哪些操作需要放在一个事务中,谨慎的使用该注解。
- Hibernate会将tinyint(1)视为boolean型,定义表时应注意,一般的非boolean需求,定义为tinyint(4).
- 一个方法仅实现一个功能。
